-
Notifications
You must be signed in to change notification settings - Fork 641
[Improvement] Set polling URL, dynamic dropdowns and improve OAuth authorization #8901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here: https://app.greptile.com/review/github.
💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!
14 file(s) reviewed, no comment(s)
Edit PR Review Bot Settings | Greptile
Warning Rate limit exceeded@rahul-rocket has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 9 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (9)
Summary by CodeRabbit
Summary by CodeRabbit
WalkthroughThis set of changes delivers a comprehensive overhaul and extension of the Zapier integration, focusing on robust OAuth2 authorization code flow, secure domain validation, and dynamic dropdown support for Zapier actions and triggers. The update introduces new event-driven handlers for timer events, a dedicated in-memory service for managing OAuth codes with domain and instance controls, and a significant refactor of controllers and services to streamline token issuance, refresh, and error handling. The Zapier plugin now supports dynamic resource selection in Zapier UI, improved sample data, and enhanced configuration via new environment variables and config interfaces. Changes
Sequence Diagram(s)sequenceDiagram
participant Zapier as Zapier
participant User as User
participant Gauzy as Gauzy App
participant AuthCodeSvc as ZapierAuthCodeService
Zapier->>Gauzy: GET /authorize (with redirect_uri, state, etc.)
Gauzy->>Gauzy: Validate client_id, response_type, redirect_uri domain
Gauzy-->>User: Redirect to login page (with Zapier params)
User->>Gauzy: Login
Gauzy->>AuthCodeSvc: generateAuthCode(userId, tenantId, orgId, redirectUri)
AuthCodeSvc-->>Gauzy: Auth code
Gauzy-->>Zapier: Redirect to redirect_uri with code & state
Zapier->>Gauzy: POST /token (with code, client_id, client_secret)
Gauzy->>AuthCodeSvc: getUserInfoFromAuthCode(code, redirectUri)
AuthCodeSvc-->>Gauzy: user info or error
Gauzy->>Gauzy: createIntegration(user info, client credentials)
Gauzy-->>Zapier: access_token, refresh_token, expires_in
sequenceDiagram
participant Gauzy
participant EventBus
participant ZapierWebhookService
participant WebhookSubscriber
Gauzy->>EventBus: TimerStartedEvent/TimerStoppedEvent
EventBus->>ZapierTimerStartedHandler: handle(event)
ZapierTimerStartedHandler->>ZapierWebhookService: notifyTimerStatusChanged(timerData)
ZapierWebhookService->>WebhookSubscriber: POST timer.status.changed event
Note right of WebhookSubscriber: Receives timer status change notification
Possibly related PRs
Suggested reviewers
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 26
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (14)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
(3 hunks)packages/plugins/integration-zapier/src/lib/zapier.module.ts
(3 hunks)packages/plugins/integration-zapier/zapier/.gitignore
(1 hunks)packages/plugins/integration-zapier/zapier/src/authentication.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
(4 hunks)packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts
(4 hunks)packages/plugins/integration-zapier/zapier/src/index.ts
(2 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/tenantList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
(2 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (1)
packages/core/src/lib/entity-subscription/entity-subscription.service.ts (1)
unsubscribe
(76-100)
packages/plugins/integration-zapier/zapier/src/index.ts (1)
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (1)
timerStatusKey
(97-97)
🪛 GitHub Check: Cspell
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
[warning] 54-54:
Unknown word (lastlog)
packages/plugins/integration-zapier/zapier/src/authentication.ts
[warning] 24-24:
Unknown word (acces)
🪛 GitHub Actions: Check Spelling and Typos with cspell
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
[warning] 54-54: Unknown word (lastlog)
packages/plugins/integration-zapier/zapier/src/authentication.ts
[warning] 24-24: Unknown word (acces)
🪛 Biome (1.9.4)
packages/plugins/integration-zapier/zapier/src/authentication.ts
[error] 29-29: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 32-32: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
[error] 1-1: A Node.js builtin module should be imported with the node: protocol.
Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.
(lint/style/useNodejsImportProtocol)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: test
- GitHub Check: build
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (22)
packages/plugins/integration-zapier/zapier/.gitignore (1)
43-43
: Good addition to ignore TypeScript build info files.Adding
*.tsbuildinfo
to the gitignore is a good practice. These files are generated by the TypeScript compiler for incremental builds and should not be committed to version control.packages/plugins/integration-zapier/zapier/src/index.ts (2)
11-17
: Well-structured imports for dynamic dropdown functionality.The addition of these hidden triggers for dynamic dropdowns is a good improvement to the Zapier integration, allowing for more interactive and user-friendly selection of related entities.
31-38
: Good implementation of hidden triggers for dynamic dropdowns.These hidden triggers are properly added to the app definition, maintaining a consistent pattern with the existing triggers. The clear comment explaining their purpose enhances code readability.
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts (1)
27-52
: Good configuration of the hidden trigger for dynamic dropdown.The configuration is well-structured with appropriate metadata, input fields, and sample data. The use of a dynamic reference to
organization_list
for the organization ID field is particularly useful.packages/plugins/integration-zapier/zapier/src/authentication.ts (2)
10-16
: Good enhancement to the authentication test configuration.Adding the method and headers with the Bearer token improves the authentication test by properly validating the access token against the API.
19-40
: Good addition of the connectionLabel method.The connectionLabel method enhances the user experience by providing a meaningful label for the connection in the Zapier UI, based on the user's information. The error handling ensures a fallback label is always provided.
🧰 Tools
🪛 Biome (1.9.4)
[error] 29-29: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 32-32: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 GitHub Check: Cspell
[warning] 24-24:
Unknown word (acces)🪛 GitHub Actions: Check Spelling and Typos with cspell
[warning] 24-24: Unknown word (acces)
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (2)
12-27
: Improved response handling for both array and object formats.The new code properly handles both single object and array responses, ensuring consistent data structure for Zapier.
109-146
: Updated sample data structure with detailed timer information.The sample data now accurately represents the timer status payload with comprehensive details like duration, running state, and detailed log information.
packages/plugins/integration-zapier/src/lib/zapier.module.ts (3)
3-3
: Switched from command/query pattern to event-driven approach.The imports have been updated to include
EventsHandler
and localEventHandlers
, indicating a shift to an event-driven architecture.Also applies to: 25-25
49-49
: Changed from command/query handlers to event handlers.The module now uses event handlers instead of command and query handlers, aligning with the event-driven approach.
51-51
:❓ Verification inconclusive
Removed TimerModule from exports.
TimerModule has been removed from the exports array, which could potentially break dependent code.
🏁 Script executed:
#!/bin/bash # Check if any files are importing TimerModule from ZapierModule echo "Checking for imports of TimerModule from ZapierModule..." rg --type ts "ZapierModule.*TimerModule|import.*TimerModule.*from.*zapier" --glob "!**/zapier.module.ts"Length of output: 229
Action Required: Verify TimerModule Removal Impact
The current search for TimerModule usage outside of the Zapier module did not yield any results, suggesting that no dependent code is directly importing TimerModule from ZapierModule. However, because the verification output was inconclusive (with no output produced), please manually verify that no indirect dependency exists that relies on TimerModule being re-exported from this module.
- File Affected:
packages/plugins/integration-zapier/src/lib/zapier.module.ts
(Line 51)- Change Noted:
TimerModule
was removed from the exports array.- Next Steps: Manually inspect dependent code or run additional repository-wide searches for "TimerModule" usage to ensure its removal won’t inadvertently break any functionality.
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (1)
5-6
: Added HttpService and RxJS for webhook notifications.The service now properly injects the HttpService and imports RxJS operators for error handling in the new notification method.
Also applies to: 15-15
packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts (6)
10-10
: Good addition of Content-Type headerAdding the Content-Type header ensures the API server correctly processes the request body as JSON.
19-19
: Proper boolean conversionConverting the string 'true' to a boolean value is necessary since Zapier typically passes form values as strings. This ensures the API receives the correct data type.
45-60
: Well-structured dynamic dropdown fieldsThe tenant and organization fields now utilize dynamic dropdowns with helpful guidance text, which greatly improves the user experience.
89-93
: Improved source field with Zapier defaultSetting the default source to 'ZAPIER' instead of 'BROWSER' makes more sense for this integration.
94-141
: Enhanced input fields with dynamic dropdownsThe addition of descriptive fields with dynamic dropdowns for projects, tasks, contacts, and teams significantly improves the usability of the integration.
144-158
: Updated sample data for better documentationThe sample data has been updated to reflect the actual response structure, which is helpful for users and developers.
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts (4)
10-10
: Good addition of Content-Type headerAdding the Content-Type header ensures the API server correctly processes the request body as JSON.
19-19
: Proper boolean conversionConverting the string 'true' to a boolean value is necessary since Zapier typically passes form values as strings. This ensures the API receives the correct data type.
45-60
: Well-structured dynamic dropdown fieldsThe tenant and organization fields now utilize dynamic dropdowns with helpful guidance text, which greatly improves the user experience.
93-140
: Enhanced input fields with dynamic dropdownsThe addition of descriptive fields with dynamic dropdowns for projects, tasks, contacts, and teams significantly improves the usability of the integration.
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/authentication.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/authentication.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/authentication.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
Outdated
Show resolved
Hide resolved
View your CI Pipeline Execution ↗ for commit d7ca3da.
☁️ Nx Cloud last updated this comment at |
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
c2cebf6
to
b43a04f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
(updates since last review)
This PR implements event handlers for timer events in the Zapier integration, enabling real-time notifications when timers are started or stopped.
- Added
ZapierTimerStartedHandler
andZapierTimerStoppedHandler
that notify Zapier webhooks when timer status changes - Implemented dynamic dropdown triggers for entities (tenants, organizations, projects, tasks, contacts, teams) to improve UX in Zapier interface
- Added
notifyTimerStatusChanged
method toZapierWebhookService
to send webhook notifications with proper error handling - Added polling fallback in
timerStatus.ts
for when webhooks aren't available, using the/api/timesheet/timer/status/worked
endpoint - Improved data handling in timer triggers to support both single objects and arrays, ensuring proper formatting for Zapier
Greptile AI: I've analyzed the changes to the Zapier integration plugin and focused on the most significant improvements that weren't covered in previous reviews.
25 file(s) reviewed, 23 comment(s)
Edit PR Review Bot Settings | Greptile
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
🔭 Outside diff range comments (1)
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (1)
93-152
: 🛠️ Refactor suggestionPotential mismatch between newly generated auth code and redirect parameters.
Inside
callback
, a new authorization code is generated (line 128), but the redirect query is built usingquery.code
instead of the freshly mintedcode
. This could prevent the intended code from being passed back to Zapier.Consider updating the redirect logic to include the new code:
- const queryParamsString = buildQueryString({ - code: query.code, - state: query.state - }); + const queryParamsString = buildQueryString({ + code, + state: query.state + });
♻️ Duplicate comments (4)
packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts (1)
16-31
: Add validation for API response structure.The code already includes validation for the API response structure, which is a good practice for ensuring robustness when dealing with external APIs.
packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts (1)
3-30
: 🧹 Nitpick (assertive)Strengthen data validation before mapping tasks
The function properly validates project ID and has good error handling, but should check if response.data and response.data.items exist before mapping.
const perform = async (z: ZObject, bundle: Bundle) => { const projectId = bundle.inputData.projectId; if(!projectId) { throw new Error('Project ID is required') } try { const response = await z.request({ url: `${process.env.API_BASE_URL}/api/tasks`, method: 'GET', headers: { Authorization: `Bearer ${bundle.authData['access_token']}`, }, params: { projectId: projectId } }); + if (!response.data || !Array.isArray(response.data.items)) { + throw new Error('Unexpected API response format'); + } return response.data.items.map((task: { id: string; title?: string; name?: string }) => ({ id: task.id, name: task.title || task.name })); } catch (error) { z.console.error('Error fetching tasks:', error); - throw new Error('Failed to fetch tasks'); + throw new Error(`Failed to fetch tasks: ${error.message || 'Unknown error'}`); } };packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts (2)
81-91
: Inconsistency in source field choices and defaultThe source field has inconsistent configuration:
- Default value is set to 'BROWSER' (line 90)
- 'ZAPIER' option is missing from the choices list
From the context and other changes, it appears 'ZAPIER' should be an available option and possibly the default.
choices: { BROWSER: 'Browser', DESKTOP: 'Desktop', MOBILE: 'Mobile', BROWSER_EXTENSION: 'Browser Extension', HUBSTAFF: 'Hubstaff', UPWORK: 'Upwork', TEAMS: 'Teams', - CLOC: 'Cloc' + CLOC: 'Cloc', + ZAPIER: 'Zapier' }, - default: 'BROWSER', + default: 'ZAPIER', helpText: 'The source of this timer entry'
149-149
: Inconsistency in sample dataThe sample data shows source as 'BROWSER' but should be consistent with the default setting for the field.
- source: 'BROWSER', + source: 'ZAPIER',
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (23)
packages/core/src/lib/auth/auth.controller.ts
(2 hunks)packages/core/src/lib/core/context/request-context.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/handlers/index.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-started.handler.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-stopped.handler.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
(5 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier.config.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.controller.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier.module.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier.service.ts
(5 hunks)packages/plugins/integration-zapier/zapier/src/authentication.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
(3 hunks)packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts
(4 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/tenantList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/plugins/integration-zapier/src/lib/zapier.controller.ts (1)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8863
File: packages/plugins/integration-zapier/src/lib/zapier.controller.ts:14-17
Timestamp: 2025-04-09T19:08:05.034Z
Learning: In the Ever Gauzy codebase, endpoints related to Zapier integration (and likely other integrations) are intentionally restricted to users with INTEGRATION_ADD and INTEGRATION_EDIT permissions, even for read-only operations. This is by design to ensure only users with integration management privileges can access integration data.
🧬 Code Graph Analysis (6)
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (1)
packages/core/src/lib/entity-subscription/entity-subscription.service.ts (1)
unsubscribe
(76-100)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (2)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (1)
Injectable
(8-118)packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (1)
packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)
packages/plugins/integration-zapier/src/lib/zapier.module.ts (2)
packages/plugins/integration-zapier/src/lib/repository/mikro-orm-zapier.repository.ts (1)
MikroOrmZapierWebhookSubscriptionRepository
(4-4)packages/plugins/integration-zapier/src/lib/handlers/index.ts (1)
EventHandlers
(5-5)
packages/core/src/lib/core/context/request-context.ts (1)
packages/contracts/src/lib/user.model.ts (1)
IUser
(40-82)
packages/plugins/integration-zapier/src/lib/zapier.service.ts (3)
packages/contracts/src/lib/zapier.model.ts (2)
IZapierAccessTokens
(3-8)ICreateZapierIntegrationInput
(12-18)packages/contracts/src/lib/integration.model.ts (1)
IIntegrationEntitySetting
(20-26)packages/plugins/integration-zapier/src/lib/zapier.config.ts (1)
ZAPIER_TOKEN_EXPIRATION_TIME
(17-17)
🪛 Biome (1.9.4)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
[error] 28-28: Don't use 'String' as a type.
Use lowercase primitives for consistency.
Safe fix: Use 'string' instead
(lint/complexity/noBannedTypes)
packages/plugins/integration-zapier/src/lib/zapier.service.ts
[error] 82-82: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (62)
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts (1)
1-53
: Good implementation of the organization team list trigger.The trigger is well-structured with proper error handling, input validation, and appropriate return of response data. The dynamic dropdown configuration is correctly set up with the organization list reference.
packages/plugins/integration-zapier/src/lib/handlers/index.ts (1)
1-5
: LGTM. Clean export of event handlers.The file correctly imports and exports the Zapier timer event handlers in a well-structured array, following good module organization practices.
packages/plugins/integration-zapier/src/lib/zapier.config.ts (2)
7-9
: LGTM. Well-defined callback URL constant.The
ZAPIER_CALLBACK_URL
is properly defined and documented with an appropriate JSDoc comment, using the environment variable for the API base URL.
16-17
: LGTM. Appropriate token expiration time constant.The
ZAPIER_TOKEN_EXPIRATION_TIME
is set to a reasonable value (1 hour) and documented clearly.packages/core/src/lib/core/context/request-context.ts (1)
219-249
:❓ Verification inconclusive
Ensure
organizations
array contains the expected structure.The method provides a good fallback mechanism to determine the current organization ID. However, when accessing
user.organizations[0].organizationId
, verify that theIUserOrganization
interface has anorganizationId
property.
🏁 Script executed:
#!/bin/bash # Check the structure of the IUserOrganization interface rg -A 15 "interface IUserOrganization" --type typescriptLength of output: 93
Attention: Verify the IUserOrganization Interface Structure
The current implementation of
RequestContext.currentOrganizationId
correctly attempts to determine the current organization ID by checking several user properties. However, since the fallback logic relies on accessinguser.organizations[0].organizationId
, please ensure that theIUserOrganization
interface indeed includes theorganizationId
property.To verify this, note that the initial attempt to search for the interface using a file type filter (
--type typescript
) resulted in an error. Please run the following command (without file type restrictions) to check the definition:Manually verify that the output shows an
organizationId
property within theIUserOrganization
interface. Once confirmed, no further changes to the fallback logic in this method are needed.
🏁 Script executed:
#!/bin/bash rg -A 15 "interface IUserOrganization" .Length of output: 1021
Action Required: Verify the Presence of
organizationId
in the User Organization InterfacesThe fallback logic in
currentOrganizationId()
appears sound. However, since theIUserOrganization
interface extendsIBaseUserOrganization
without explicitly specifying any properties, please confirm that theorganizationId
property is defined—either directly onIUserOrganization
or within its base interface (IBaseUserOrganization
). The grep results indicate that the property isn’t directly declared inIUserOrganization
, so you'll need to verify the structure ofIBaseUserOrganization
.
- Steps:
- Manually inspect or run an additional search (e.g., using
rg -A 20 "interface IBaseUserOrganization" .
) to locate the definition oforganizationId
withinIBaseUserOrganization
.- If the property is missing, update the interfaces accordingly to ensure that the fallback in
currentOrganizationId()
works as expected.packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (3)
83-83
: Good addition of a class-level logger instance.Using a dedicated logger instance with the class name improves consistency and traceability of log messages.
91-99
: Good implementation of OAuth credentials validation.The added code properly checks for Zapier OAuth credentials and provides appropriate feedback. This will help administrators quickly identify configuration issues during setup.
108-108
: Good consistency improvement in logging.Changing from console.log to the class logger instance improves consistency in logging and follows better practices for production code.
packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-started.handler.ts (4)
1-5
: Good implementation of imports and dependencies.The imports are properly organized and include all necessary dependencies from NestJS CQRS, Gauzy core, and local services.
6-9
: Correctly implemented class declaration with appropriate decorators.The class correctly implements the
IEventHandler
interface and is decorated with both@Injectable()
and@EventsHandler(TimerStartedEvent)
to properly register it in the NestJS dependency injection system and event handling system.
10-15
: Well-documented method with JSDoc.The JSDoc comments clearly explain the purpose and parameters of the handler method, which is good practice for maintainability.
16-22
: Correctly implemented event handler logic.The handler properly processes the TimerStartedEvent and forwards the relevant data to the webhook service. The structure follows good practices for event handling in NestJS.
packages/plugins/integration-zapier/zapier/src/authentication.ts (3)
4-4
: Proper import of Zapier types.The import of ZObject and Bundle types from zapier-platform-core ensures proper type checking, improving code quality and preventing potential errors.
10-16
: Enhanced authentication test configuration.The improved test configuration properly defines the method and headers with bearer token authentication, which is essential for validating OAuth2 tokens correctly.
18-41
: Well-implemented connection label method with proper error handling.The connectionLabel method effectively fetches user data and provides appropriate fallbacks. I see you've addressed previous review comments by:
- Using optional chaining for safer property access
- Ensuring consistent naming in the fallback connection label
- Correctly using the access_token property name
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts (5)
3-7
: Well-defined Contact interface.The Contact interface properly defines the expected structure of contact objects with required properties and an index signature for additional properties, improving type safety.
9-15
: Good input validation.The function properly validates the required organizationId parameter and throws a descriptive error if it's missing, which is essential for providing a good user experience.
16-30
: Proper API request with response validation.The function makes an API request with correct authorization headers and validates the API response format, addressing a previous review comment. This ensures more robust error handling if the API changes.
31-38
: Effective error handling and data transformation.The function properly transforms the API response to a simpler structure and includes comprehensive error handling with informative error messages and console logging for debugging.
41-65
: Well-structured trigger definition.The trigger definition follows Zapier best practices with:
- Clear identification (key, noun)
- User-friendly display properties
- Properly configured input fields with dynamic dropdown
- Sample data for testing
The hidden property ensures it's only used for dynamic dropdowns and not exposed directly to users.
packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-stopped.handler.ts (1)
1-24
: Good implementation of the event handler!The handler follows NestJS best practices with proper dependency injection and event handling. The code is well-documented with clear JSDoc comments.
For future enhancement, consider adding explicit error handling within the
handle
method to gracefully manage notification failures, especially if notifications are critical to your application flow.packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts (2)
9-31
: Well-structured API request with proper validation.The implementation correctly validates required inputs and API response structure, following best practices for Zapier triggers.
42-67
: Good trigger configuration with clear documentation.The trigger configuration is well structured with appropriate metadata, labels, and help text. The dynamic dropdown for organization selection provides a good user experience.
packages/plugins/integration-zapier/zapier/src/triggers/tenantList.ts (2)
3-7
: LGTM! Well-defined Tenant interfaceThe Tenant interface properly defines the structure expected from the API response with appropriate types.
33-48
: LGTM! Well-structured export with clear metadataThe export definition provides all necessary Zapier-specific metadata and correctly sets
hidden: true
for this dynamic dropdown component.packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts (1)
32-57
: LGTM! Well-structured trigger with dynamic field dependenciesThe trigger definition is well-structured with clear labels and appropriate field configurations. The dynamic dropdown dependency for projects is correctly implemented.
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (2)
109-109
: LGTM! performList added to operationsGood addition of the performList function to the operations object, providing a polling fallback when webhooks aren't available.
113-146
: LGTM! Comprehensive sample data structureThe updated sample data structure provides a detailed representation of timer status including the lastLog object, which helps Zapier users understand the data format.
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (2)
10-18
: LGTM! Well-structured in-memory storage for auth codesThe in-memory Map implementation with clear type definitions is appropriate for temporary auth codes.
51-69
: LGTM! Secure implementation of code validationGood implementation of the code validation logic with immediate deletion after use to prevent reuse attacks.
packages/plugins/integration-zapier/src/lib/zapier.controller.ts (2)
16-16
: Import looks safe.
No concerns with addingIZapierAccessTokens
here, assuming it may be used in future additions.
80-101
: Robust error handling approach.
Re-throwing known exceptions and defaulting to a 500-level error for unknown cases is clear and consistent.packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (2)
5-6
: New imports for HTTP and RxJS operators look correct.
These additions are necessary for making HTTP requests and handling stream operations.
14-14
: Injection of HttpService is appropriate.
Leveraging the service for webhook notifications aligns with NestJS best practices.packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts (10)
10-10
: JSON content type is correct.
Ensures proper serialization and deserialization for the request payload.
41-41
: Updated description is concise.
A simple and clear explanation of what the create does.
45-60
: Dynamic fields for tenant and organization.
Adding these improves user guidance and ensures correct data selection.
90-92
: Help text forsource
provides clarity.
It’s beneficial to explain how the source field is used.
93-99
: Description field adds flexibility.
Allows users to specify details about the tracked work.
109-140
: Project, task, contact, and team fields bolster customization.
Dynamically populated options improve user experience.
144-146
: Sample data now aligns with the new structure.
Demonstrates realistic usage for tenant and organization fields.
150-150
: Providing a descriptive message fordescription
.
Makes the sample usage clearer.
153-153
: UpdatedprojectId
reflects the new field.
Good consistency with the prior input definitions.
156-156
: AddedorganizationTeamId
in sample data.
Ensures users see how to include team information.packages/plugins/integration-zapier/src/lib/zapier.module.ts (2)
25-26
: Good introduction of new event-driven and auth code services.Replacing command/query handlers with
EventHandlers
aligns with the shift toward an event-driven architecture, and importingZapierAuthCodeService
nicely complements that approach.
45-53
: Providers and exports alignment looks solid.
ZapierAuthCodeService
andEventHandlers
are properly registered. Exporting only the needed services helps maintain a clean public API.packages/plugins/integration-zapier/src/lib/zapier.service.ts (5)
1-5
: New imports for exceptions and UUID generation are appropriate.Using built-in NestJS exceptions and UUIDs is a solid approach for robust error handling and token generation.
17-17
: Typed return for IZapierAccessTokens enhances clarity.Specifying
IZapierAccessTokens
for the token-related methods helps ensure consistent handling of token data.
26-26
: Importing config constants is consistent with the rest of the module.Using
ZAPIER_AUTHORIZATION_URL
andZAPIER_TOKEN_EXPIRATION_TIME
fromzapier.config
centralizes configuration logic.
30-30
: Using a dedicated logger improves observability.Capturing errors and key events via
Logger
aids in debugging and maintenance.
146-238
: NewcreateIntegration
method is clear and well-structured.Creating a fresh integration with tokens, populating default entity settings, and updating or creating the
IntegrationTenant
are handled thoroughly. The approach to unify creation and token generation fosters maintainability.packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (3)
42-76
: Comprehensive checks inauthorize
method.
- Validating
zapier_redirect_uri
,state
,response_type
, andclient_id
ensures a secure OAuth flow.- Redirecting to the login page with relevant parameters is a sensible approach.
153-217
:getToken
method properly validates and creates integration tokens.
- Validating grant type, client credentials, and the authorization code is thorough.
- The use of
this.zapierService.createIntegration
standardizes token creation across the module.
219-278
:refreshToken
endpoint is aligned with service logic.
- Properly checks grant type and client credentials.
- Delegates to
zapierService.refreshToken
for consistent token refreshing.packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts (8)
10-10
: Good addition of the Content-Type headerAdding the explicit 'Content-Type' header ensures proper handling of the JSON request body by the API.
19-19
: Improved boolean conversion for isBillableThe strict equality check
=== 'true'
is a good improvement for explicit boolean conversion.
41-41
: Clear description for the Zapier actionThe updated description clearly explains what this action does in Gauzy.
45-60
: Good implementation of dynamic dropdown for tenant and organizationThe dynamic dropdowns for tenant and organization IDs with clear help text improve user experience significantly.
93-99
: Good addition of description fieldAdding a description field with appropriate help text improves the usability of the timer functionality.
100-107
: Improved isBillable field configurationThe billable field has been properly configured with the correct type (boolean) and helpful description.
109-140
: Excellent implementation of dynamic dropdowns for related entitiesThe implementation of dynamic dropdowns for project, task, contact, and team entities greatly improves the user experience by allowing selection from existing records rather than requiring manual ID entry.
144-146
: Sample data properly updated with realistic valuesThe sample response has been properly updated with realistic UUID values and additional fields.
Also applies to: 150-156
packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
♻️ Duplicate comments (4)
packages/core/src/lib/auth/auth.controller.ts (1)
169-169
: 🧹 Nitpick (assertive)Consider using a dedicated DTO for improved type safety.
While the inline type
{ zapier_state?: string }
is better thanany
, using a dedicated DTO would further improve type safety and enable validation with class-validator decorators.+// Add this to your DTOs +export class ZapierOAuthQueryDTO { + @IsString() + @IsOptional() + zapier_redirect_uri?: string; + + @IsString() + @IsOptional() + zapier_state?: string; +} -async loginSuccess(@Query() query: { zapier_state?: string }, @Res() res: Response) { +async loginSuccess(@Query() query: ZapierOAuthQueryDTO, @Res() res: Response) {packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts (1)
29-32
: 🛠️ Refactor suggestionInclude original error details in the error message.
When catching errors, you log the original error but throw a generic error message. Including the original error details would make troubleshooting easier.
} catch (error) { z.console.error('Error fetching tasks:', error); - throw new Error('Failed to fetch tasks'); + throw new Error(`Failed to fetch tasks: ${error.message || 'Unknown error'}`); }packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts (1)
17-17
:⚠️ Potential issueFix the fallback URL logic.
The current fallback URL implementation won't work as expected. String interpolation will always produce a string (even empty), which is truthy in JavaScript/TypeScript.
- const baseUrl = `${process.env.API_BASE_URL}` || 'http://localhost:3000'; + const baseUrl = process.env.API_BASE_URL || 'http://localhost:3000';packages/plugins/integration-zapier/src/lib/zapier.module.ts (1)
14-15
: Re-check ifTimerModule
is needed.Since there's a past comment about removing
TimerModule
if it's unused, please verify whether it's actually utilized or exported. If it's no longer necessary, removing it will reduce complexity.Run this script to see if
TimerModule
is referenced anywhere else:#!/bin/bash fd -e ts 'TimerModule' --exec rg 'TimerModule' {}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (18)
packages/core/src/lib/auth/auth.controller.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
(5 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier.controller.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.module.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier.service.ts
(5 hunks)packages/plugins/integration-zapier/zapier/src/authentication.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
(4 hunks)packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts
(4 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/tenantList.ts
(1 hunks)packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts
(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts (1)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts:100-107
Timestamp: 2025-04-10T15:31:05.841Z
Learning: In Zapier integrations, boolean configuration fields need to use type 'boolean' but with string default values like 'true' or 'false' because that's what the Zapier platform expects to receive. The actual boolean conversion is handled in the code with a comparison like `inputData['fieldName'] === 'true'`.
🧬 Code Graph Analysis (8)
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (1)
packages/core/src/lib/entity-subscription/entity-subscription.service.ts (1)
unsubscribe
(76-100)
packages/core/src/lib/auth/auth.controller.ts (1)
packages/common/src/lib/decorators/public.decorator.ts (1)
Public
(15-15)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (3)
packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-started.handler.ts (1)
Injectable
(6-23)packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-stopped.handler.ts (1)
Injectable
(6-24)packages/plugins/integration-zapier/src/lib/repository/type-orm-zapier.repository.ts (1)
Injectable
(6-13)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (2)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (1)
Injectable
(8-118)packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)
packages/plugins/integration-zapier/src/lib/zapier.module.ts (2)
packages/plugins/integration-zapier/src/lib/repository/mikro-orm-zapier.repository.ts (1)
MikroOrmZapierWebhookSubscriptionRepository
(4-4)packages/plugins/integration-zapier/src/lib/handlers/index.ts (1)
EventHandlers
(5-5)
packages/plugins/integration-zapier/src/lib/zapier.controller.ts (2)
packages/core/src/lib/shared/decorators/permissions.decorator.ts (1)
Permissions
(5-5)packages/contracts/src/lib/zapier.model.ts (1)
IZapierEndpoint
(20-24)
packages/plugins/integration-zapier/src/lib/zapier.service.ts (3)
packages/contracts/src/lib/zapier.model.ts (2)
IZapierAccessTokens
(3-8)ICreateZapierIntegrationInput
(12-18)packages/contracts/src/lib/integration.model.ts (1)
IIntegrationEntitySetting
(20-26)packages/plugins/integration-zapier/src/lib/zapier.config.ts (1)
ZAPIER_TOKEN_EXPIRATION_TIME
(17-17)
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (3)
packages/plugins/integration-zapier/src/lib/dto/create-zapier-integration.dto.ts (1)
CreateZapierIntegrationDto
(6-31)packages/contracts/src/lib/zapier.model.ts (2)
IZapierAccessTokens
(3-8)ZapierGrantType
(10-10)packages/plugins/integration-zapier/src/lib/zapier.config.ts (1)
ZAPIER_TOKEN_EXPIRATION_TIME
(17-17)
🪛 Biome (1.9.4)
packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
[error] 22-22: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts
[error] 31-31: This code is unreachable
... because either this statement will return from the function ...
... or this statement will throw an exception beforehand
(lint/correctness/noUnreachable)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (63)
packages/core/src/lib/auth/auth.controller.ts (2)
11-14
: LGTM! Necessary imports added for new functionality.The added imports are appropriate for the new Zapier OAuth flow implementation:
BadRequestException
for error handling,Res
for response manipulation, and theResponse
type from Express for proper typing.
154-187
: Well implemented Zapier OAuth success handler with proper security measures.The implementation properly handles the Zapier OAuth flow with appropriate error handling, security considerations, and fallback paths. The code successfully addresses previous review comments by:
- Including the
@Public()
decorator to make the endpoint accessible without authentication- Adding proper validation for
API_BASE_URL
with a fallback value- Sanitizing the
zapier_state
parameter withencodeURIComponent
- Implementing error logging before redirecting
The method appropriately differentiates between Zapier OAuth flow and regular login flow based on the presence of the
zapier_state
parameter.packages/plugins/integration-zapier/zapier/src/authentication.ts (2)
10-16
: Well-structured authentication test configuration.The test configuration is now properly structured with clear method, URL, and authorization headers. This improves the authentication testing process.
19-40
: Good implementation of the connectionLabel method.The connectionLabel method provides a user-friendly way to identify connections in the Zapier UI. The implementation correctly:
- Fetches user data from the API
- Creates a label based on user name or email with fallback
- Uses proper error handling with try/catch
- Handles unexpected data structures with optional chaining
This is a significant improvement to the user experience.
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts (3)
1-7
: Good use of TypeScript interface.The Contact interface provides clear typing for the contact objects, which improves code readability and type safety.
16-38
: Well-implemented error handling and response validation.The perform function includes robust error handling with:
- Proper API request with authentication
- Response structure validation
- Error logging and meaningful error messages
- Try/catch block to prevent unhandled exceptions
This implementation ensures reliable behavior even when the API response is unexpected.
41-66
: Well-configured dynamic dropdown trigger.The trigger is properly configured as a hidden dynamic dropdown with:
- Clear labels and descriptions
- Required field validation
- Dynamic reference to organization list
- Sample data for testing
This implementation follows Zapier's best practices for dynamic dropdowns.
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (3)
77-77
: Good use of class-level logger.Adding a class-level logger instance improves consistency and follows best practices for NestJS applications.
85-93
: Improved OAuth credentials validation.The added code properly validates the presence of Zapier OAuth credentials and provides clear warning messages when they're missing, which helps with troubleshooting.
102-102
: Consistent logging approach.Using the class logger for plugin destruction logging is more consistent with NestJS practices than using console.log directly.
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts (2)
1-7
: Good use of TypeScript interface.The OrganizationTeam interface provides clear typing for the team objects, improving code readability and type safety.
37-62
: Well-configured dynamic dropdown trigger.The trigger is properly configured as a hidden dynamic dropdown with:
- Clear labels and descriptions
- Required field validation
- Dynamic reference to organization list
- Sample data for testing
This implementation follows Zapier's best practices for dynamic dropdowns.
packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts (1)
40-41
: Description clearly identifies dynamic dropdown purpose.The description now clearly indicates that this trigger is for dynamic dropdown selection, which helps with developer understanding.
packages/plugins/integration-zapier/zapier/src/triggers/tenantList.ts (3)
19-26
: Good error handling with comprehensive validation.Your implementation includes thorough response validation:
- Checking if the response exists
- Validating the expected data structure
This prevents potential runtime errors and provides clear error messages.
32-35
: Well implemented error handling with detailed error messages.The error handling includes the original error message which is excellent for troubleshooting issues.
38-53
: Well structured trigger configuration.The trigger configuration is complete with appropriate metadata, clear descriptions, and a representative sample. The hidden property correctly indicates this is for dynamic dropdowns.
packages/plugins/integration-zapier/zapier/src/triggers/organizationList.ts (3)
24-25
: Good use of object shorthand property.Using the shorthand syntax for the
params
object makes the code more concise and follows modern JavaScript best practices.
27-30
: Good response validation implementation.Validating both the existence and structure of the response data prevents potential runtime errors and provides a clear error message for troubleshooting.
36-38
: Good error handling with detailed error message.Including the original error message in the thrown error makes troubleshooting much easier.
packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts (2)
28-30
: Good response validation implementation.Validating both the existence and structure of the response data prevents potential runtime errors and provides a clear error message.
36-39
: Good error handling with detailed error message.Including the original error message in the thrown error makes troubleshooting much easier.
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts (3)
24-27
: Looks good.
Mapping the array items is straightforward and ensures each item has an ID. The spread operator usage is clear and succinct.
34-37
: Clear and concise docstring.
The docstring for the fallback polling action accurately describes its purpose.
39-66
: Well-structured fallback polling function.
TheperformList
function cleanly handles potential errors and validates the response format before mapping the data. The error handling via thetry/catch
block is also consistent with the rest of the codebase.packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (2)
5-6
: Good use of RxJS imports.
ImportingcatchError
,firstValueFrom
, andof
ensures modern RxJS practices without deprecated methods liketoPromise()
.
14-14
: HTTP service injection aligned with NestJS best practices.
This underscores proper dependency injection for making external requests in a testable manner.packages/plugins/integration-zapier/zapier/src/creates/stopTimer.ts (6)
10-10
: Explicit content type is beneficial.
Passing'Content-Type': 'application/json'
ensures the server correctly interprets the request body.
19-19
: Correct string-to-boolean conversion.
Usingbundle.inputData['isBillable'] === 'true'
is consistent with the known Zapier platform requirement for boolean fields in string format. This is aligned with your recorded learnings.
28-31
: Error handling block is solid.
Logging the error within thecatch
block and rethrowing a new error with context is a clean pattern. The static analysis tool’s “unreachable code” flag for line 31 is a false positive—this simply closes the function.🧰 Tools
🪛 Biome (1.9.4)
[error] 31-31: This code is unreachable
... because either this statement will return from the function ...
... or this statement will throw an exception beforehand
(lint/correctness/noUnreachable)
41-41
: Updated description is precise and user-friendly.
Changing the text to “Stops a timer in Gauzy.” clarifies the action for users.
45-60
: Comprehensive input fields for stopping timers.
- Dynamically populating tenant, organization, project, task, contact, and team IDs enhances usability.
- Providing help text for each field is excellent for guiding Zapier users.
- Using
'false'
as the default string for the booleanisBillable
field is the correct approach given Zapier’s constraints, backed by your learnings.Also applies to: 93-99, 100-107, 109-116, 117-124, 125-132, 133-140
144-156
: Sample data accurately reflects new fields.
Including relevant IDs and an illustrative description helps users test their Zap configurations. The expanded sample is consistent with the new input fields.packages/plugins/integration-zapier/src/lib/zapier.module.ts (4)
25-25
: Confirm correct usage ofEventHandlers
.Including
EventHandlers
suggests a shift to an event-driven pattern. Verify that each handler inEventHandlers
is thoroughly tested to ensure no unhandled event logic.
26-26
: Well-structured service import.Importing
ZapierAuthCodeService
here is appropriate. Ensure it’s only provided in one module to avoid potential multiple instantiations or confusion about its lifecycle.
48-48
: Provider addition looks good.Registering
ZapierAuthCodeService
in providers allows injection across the module. Be sure to implement tests for methods that rely on this service.
53-53
: Confirm exports alignment.Exporting
ZapierAuthCodeService
alongsideZapierService
andZapierWebhookService
is helpful for consumers outside this module. Make sure there are no unintentional exports and that other modules import only what they need.packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (2)
29-53
: Validate concurrency usage forMap
.The
generateAuthCode
method internally mutates a sharedMap
. If multiple requests come in simultaneously, consider concurrency-safe operations or external locking mechanisms to avoid potential race conditions in high-throughput scenarios.
61-84
: Good practice of one-time code usage.In
getUserInfoFromAuthCode
, you delete the code after a successful check, preventing replay attacks. This is a sound security choice.packages/plugins/integration-zapier/src/lib/zapier.service.ts (3)
1-30
: Imports and logger setup are fine.Bringing in
Logger
,uuidv4
, and typed interfaces clarifies your intent. The new logger property ensures consistent debugging messages.
62-120
: Refined token refresh logic.
- Error handling: Appropriately raises
NotFoundException
andBadRequestException
for missing settings.- Performance: The for-loop to collect
client_id
,client_secret
, andrefresh_token
is more performant than using a.reduce()
with spreads.- Security: Generating new tokens with
uuidv4()
is straightforward, but confirm if any cryptographic randomness or hashing is desired.Overall, these changes look robust.
142-234
: Robust creation of Zapier integration.
- Integration tenant: Using
IntegrationTenantUpdateOrCreateCommand
for consistent domain logic is a good approach.- Dynamic entity settings: Mapping relevant organization and tenant IDs ensures each tied entity belongs to the correct scope.
- Export testing: Ensure a thorough test includes verifying the final stored settings (e.g.,
client_id
,client_secret
,access_token
, etc.).No critical concerns; these improvements nicely enhance the Zapier integration flow.
packages/plugins/integration-zapier/src/lib/zapier.controller.ts (6)
13-14
: Imports align with usage
These imports effectively introduce thePermissionsEnum
and theTenantPermissionGuard
and others used below. No issues found.
24-24
: Constructor injection looks fine
ThezapierService
is injected and used, and there are no obvious issues here.
26-34
: Consistent API documentation
Declaring a description of "Successfully retrieved Zapier triggers" and specifying 400 for a missing/empty token is consistent with the enforced validations invalidateToken
.
35-43
: Trigger fetching logic
The new/triggers
endpoint validates the token and fetches triggers fromzapierService.fetchTriggers(token)
. The error handling is appropriate.
45-63
: Actions endpoint approach
The/actions
endpoint mirrors/triggers
, but usesvalidateToken(token, true)
, causing a different exception (UnauthorizedException
) for missing tokens. Confirm this is intentional if the design expects more stringent requirements for actions.Would you like to verify the expected behavior for missing tokens on both endpoints, ensuring the difference in thrown exceptions is truly intended?
67-68
: Token validation helper
ThevalidateToken()
method throwsBadRequestException
orUnauthorizedException
based on the boolean flag. Naming and usage look consistent, and no issues are apparent.packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (6)
1-12
: Imported modules reflect updated OAuth flow
All imported entities (exceptions, DTOs, config, services) appear to be used in the new authorization logic and token management code below.
15-18
: Controller class setup
Tagging and logger initialization are standard and pose no issues.
20-24
: Dependency injection
All injected dependencies (ConfigService
,ZapierAuthCodeService
,ZapierService
) align with the methods in this controller.
86-145
: callback() method overwrites the code
A new authorization code is generated (line 128), yet the final redirect sendsquery.code
(line 136). Ensure this is intentional and that Zapier expects or uses the original code parameter rather than the newly generated one.Would you like to confirm that sending the original
query.code
back to Zapier is correct, or should we forward the newly generated code?
153-217
: Token endpoint handles one-time codes
This approach properly validates credentials, retrieves user info tied to the code, and constructs an integration. Consider confirming that the code is invalidated or marked used to prevent reuse in undesired scenarios.
42-75
:❓ Verification inconclusive
authorize() method environmental assumptions
The method correctly checks required parameters (redirectUri
,state
,responseType
,clientId
). Make sureprocess.env['CLIENT_BASE_URL']
is consistently defined in each environment, or fallback gracefully to avoid broken redirects.
🏁 Script executed:
#!/bin/bash # Verify that CLIENT_BASE_URL is set in environment variables env | grep CLIENT_BASE_URLLength of output: 28
Action Required: Confirm CLIENT_BASE_URL Configuration
Theauthorize()
method correctly validates the required parameters. However, the verification command did not output a definedCLIENT_BASE_URL
in the current environment. Please ensure that this environment variable is set consistently in all deployment environments. If it might be unset at runtime, consider implementing a fallback or error handling mechanism to prevent broken redirects.
- Verify that
process.env['CLIENT_BASE_URL']
is set in production, staging, and other target environments.- If the variable might be undefined, add a fallback mechanism or additional logging to handle such cases gracefully.
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts (10)
10-10
: Added content type header
Explicitly setting'Content-Type': 'application/json'
is a good improvement for clarity.
19-19
: String-to-boolean conversion
This line correctly translates'isBillable'
from Zapier’s string to a boolean for the API.
41-41
: Minor description text
"For 'Starts a timer in Gauzy.'" This is consistent with the operation’s purpose.
45-52
: Tenant selection field
Using adynamic
property is a good approach for retrieving tenant IDs.
53-60
: Organization selection field
Similarly consistent dynamic usage for organizations.
88-91
: Default source mismatch
The AI summary suggests changing the default source to'ZAPIER'
, but the actual code retains'BROWSER'
.Likely an incorrect or invalid review comment.
93-99
: Added 'description' field
Optional text field is a valuable addition for specifying timer details.
100-107
: Existing isBillable approach
Definingtype: 'boolean'
with default'false'
aligns with Zapier’s string-based boolean usage.
109-140
: Extended dynamic fields
Extra fields for project, task, contact, and team. This is well-structured for capturing relevant context.
144-156
: Sample updated
The new sample includes additional fields matching the updated input structure. The defaultsource
remains'BROWSER'
in the sample, confirming the discrepancy with the summary.Likely an incorrect or invalid review comment.
packages/plugins/integration-zapier/zapier/src/triggers/organizationContactList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/organizationTeamList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/taskList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/triggers/projectList.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🔭 Outside diff range comments (3)
.env.demo.compose (1)
266-269
: 🧹 Nitpick (assertive)Spellcheck: add
POSTHOG
to cspell dictionary
The CI flaggedPOSTHOG
as an unknown word. Please add it to thecspell.json
dictionary or configure it as an allowed term to suppress this warning.🧰 Tools
🪛 GitHub Check: Cspell
[warning] 269-269:
Unknown word (POSTHOG)
[warning] 268-268:
Unknown word (POSTHOG)
[warning] 267-267:
Unknown word (POSTHOG)
[warning] 266-266:
Unknown word (POSTHOG)🪛 GitHub Actions: Check Spelling and Typos with cspell
[warning] 266-269: Unknown word 'POSTHOG' found by cspell.
.env.local (1)
250-253
: 🧹 Nitpick (assertive)Spellcheck: include
POSTHOG
in dictionary
The CI warning indicates thatPOSTHOG
is not recognized. Add it tocspell.json
or mark it as an allowed term to clear this warning.🧰 Tools
🪛 GitHub Actions: Check Spelling and Typos with cspell
[warning] 250-253: Unknown word 'POSTHOG' found by cspell.
.env.docker (1)
245-248
: 🧹 Nitpick (assertive)Spellcheck: configure
POSTHOG
in cspell
To resolve the CI spelling warning forPOSTHOG
, include it in yourcspell.json
dictionary or add an ignore rule in this file.🧰 Tools
🪛 GitHub Check: Cspell
[warning] 246-246:
Unknown word (POSTHOG)
[warning] 245-245:
Unknown word (POSTHOG)🪛 GitHub Actions: Check Spelling and Typos with cspell
[warning] 245-248: Unknown word 'POSTHOG' found by cspell.
♻️ Duplicate comments (4)
.env.compose (1)
152-155
: Remove leading spaces from environment variable definitions.The environment variables
ALLOWED_DOMAINS
,MAX_AUTH_CODES
, andINSTANCE_COUNT
have leading spaces that could cause parsing issues in some environments. Remove the leading spaces for compatibility.- ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com - MAX_AUTH_CODES=1000 - INSTANCE_COUNT=1 +ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com +MAX_AUTH_CODES=1000 +INSTANCE_COUNT=1.env.sample (1)
156-157
: Security improvement with domain allowlist in sample config.Adding the
ALLOWED_DOMAINS
variable to the sample environment file ensures developers will include this security configuration in their deployments. The included domains cover the main application domains and major integration partners.Consider adding a brief comment above this line explaining the purpose of this environment variable and how it impacts OAuth security, to help developers understand its importance when configuring their environments.
+# Comma-separated list of domains allowed for OAuth redirects (security feature) ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com
.env.demo.compose (1)
154-157
: Document new OAuth environment variables
The newALLOWED_DOMAINS
,MAX_AUTH_CODES
, andINSTANCE_COUNT
entries are correctly formatted for the demo environment. Ensure these are also reflected in.env.sample
and your documentation to maintain consistency across deployment configurations..env.docker (1)
151-154
: Document new OAuth environment variables in Docker config
The addedALLOWED_DOMAINS
,MAX_AUTH_CODES
, andINSTANCE_COUNT
entries are correctly formatted. Please verify these are also updated in.env.sample
and any project documentation to ensure coverage across all environment setups.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
.env.compose
(1 hunks).env.demo.compose
(1 hunks).env.docker
(1 hunks).env.local
(1 hunks).env.sample
(1 hunks)packages/config/src/lib/environments/environment.prod.ts
(1 hunks)packages/config/src/lib/environments/environment.ts
(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Check Spelling and Typos with cspell
.env.sample
[warning] 249-252: Unknown word 'POSTHOG' found by cspell.
.env.demo.compose
[warning] 266-269: Unknown word 'POSTHOG' found by cspell.
.env.compose
[warning] 258-261: Unknown word 'POSTHOG' found by cspell.
.env.local
[warning] 250-253: Unknown word 'POSTHOG' found by cspell.
.env.docker
[warning] 245-248: Unknown word 'POSTHOG' found by cspell.
packages/config/src/lib/environments/environment.ts
[warning] 174-178: Multiple unknown words 'posthog' and 'POSTHOG' found by cspell.
packages/config/src/lib/environments/environment.prod.ts
[warning] 174-178: Multiple unknown words 'posthog' and 'POSTHOG' found by cspell.
🪛 Biome (1.9.4)
packages/config/src/lib/environments/environment.prod.ts
[error] 205-205: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (5)
packages/config/src/lib/environments/environment.ts (3)
203-203
: Consider trimming domain strings.When parsing the environment variable
GAUZY_ALLOWED_DOMAINS
, you correctly map over the domain strings to trim them, which prevents potential validation errors from whitespace in the domain entries.
204-204
: Good usage of Number.parseInt instead of parseInt.Using
Number.parseInt
instead of the globalparseInt
follows ES2015 best practices that moved some globals into the Number namespace for consistency.
205-205
: Simplify boolean conversion.The boolean conversion for
instanceCount
can be simplified by directly returning the equality check result.- instanceCount: process.env.INSTANCE_COUNT === 'true', + instanceCount: process.env.INSTANCE_COUNT === 'true',This code already correctly implements the simpler form without using an unnecessary ternary expression.
packages/config/src/lib/environments/environment.prod.ts (2)
203-203
: Add domain trimming for consistency.The implementation is functional and includes the
.trim()
function when processing domains, which prevents validation errors from whitespace in domain entries.
204-204
: Use Number.parseInt for better consistency.The implementation correctly uses
Number.parseInt
over the globalparseInt
, which aligns with ES2015 best practices.
@@ -4,20 +4,34 @@ import { IZapierConfig } from '@gauzy/common'; | |||
/** | |||
* Register Zapier OAuth configuration using @nestjs/config | |||
*/ | |||
export default registerAs( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// zapier.config.ts
import { registerAs } from '@nestjs/config';
export default registerAs('zapier', () => ({
apiUrl: 'https://api.zapier.com/v1',
authorizationUrl: ${process.env.API_BASE_URL}/api/integration/zapier/oauth/authorize
,
callbackUrl: ${process.env.API_BASE_URL}/api/integration/zapier/oauth/callback
,
tokenUrl: ${process.env.API_BASE_URL}/api/integration/zapier/token/:integrationId
,
redirectUri: process.env.ZAPIER_REDIRECT_URI || 'https://zapier.com/dashboard/auth/oauth/return/App221848CLIAPI/',
tokenExpirationTime: 3600, // in seconds
}));
So we can move this file too inside plugin i think as it independent file. So there should be only one file called zapier.config.ts
and we define all envs here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
♻️ Duplicate comments (3)
.env.sample (1)
156-159
: 🧹 Nitpick (assertive)Add more detailed comments for auth code and instance settings.
While you've added a good comment for the allowed domains, the MAX_AUTH_CODES and INSTANCE_COUNT variables would benefit from specific explanatory comments to help developers understand their purpose.
# Comma-separated list of domains allowed for OAuth redirects (security feature) GAUZY_ZAPIER_ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com +# Maximum number of OAuth authorization codes to store in memory GAUZY_ZAPIER_MAX_AUTH_CODES=1000 +# Number of server instances (affects auth code cleanup behavior) GAUZY_ZAPIER_INSTANCE_COUNT=1packages/config/src/lib/environments/environment.prod.ts (1)
207-207
: Simplify boolean coercionThe boolean ternary is redundant and triggers the Biome lint warning.
- instanceCount: process.env.GAUZY_ZAPIER_INSTANCE_COUNT === 'true' ? true : false, + instanceCount: process.env.GAUZY_ZAPIER_INSTANCE_COUNT === 'true',🧰 Tools
🪛 Biome (1.9.4)
[error] 207-207: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (1)
259-266
: Do not reveal which credential failedAs above, use a unified message and correct status:
- throw new HttpException('Invalid client ID', HttpStatus.BAD_REQUEST); + throw new UnauthorizedException('Invalid client credentials');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (13)
.env.compose
(1 hunks).env.demo.compose
(1 hunks).env.docker
(1 hunks).env.local
(1 hunks).env.sample
(1 hunks)packages/config/src/lib/config/index.ts
(1 hunks)packages/config/src/lib/config/zapier.ts
(1 hunks)packages/config/src/lib/environments/environment.prod.ts
(1 hunks)packages/config/src/lib/environments/environment.ts
(1 hunks)packages/config/src/lib/environments/ienvironment.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
(3 hunks)
🧰 Additional context used
🧠 Learnings (3)
.env.local (1)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8863
File: packages/plugins/integration-zapier/zapier/src/authentication.ts:29-29
Timestamp: 2025-04-02T09:39:39.648Z
Learning: When developing Zapier integrations that will be deployed to the Zapier platform, use `process.env` directly to access environment variables instead of importing from `@gauzy/config`, as Zapier's platform cannot recognize or import this package.
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (5)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:27-29
Timestamp: 2025-04-15T18:29:09.314Z
Learning: The ZapierAuthCodeService in the Zapier integration plugin is designed to be initialized only once as a singleton service in the NestJS application.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:6-19
Timestamp: 2025-04-11T06:13:47.856Z
Learning: For the Zapier integration auth code storage, using an in-memory Map is acceptable for moderate usage volumes due to the built-in safeguards: a maximum limit of 1000 codes and an automatic cleanup mechanism for expired codes.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:93-105
Timestamp: 2025-04-11T09:03:32.978Z
Learning: For the Zapier integration auth code storage, on-demand cleanup (triggered when approaching the maximum code limit) is sufficient without the need for periodic scheduled cleanup, as determined by the implementation requirements.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:37-70
Timestamp: 2025-04-11T14:06:04.523Z
Learning: The ZapierAuthCodeService unit tests will be implemented in a future PR, as confirmed by RolandM99.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:145-165
Timestamp: 2025-04-15T13:43:30.444Z
Learning: The domain validation in ZapierAuthCodeService doesn't need to handle IP addresses, as OAuth redirects typically only require domain name validation.
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (2)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:21-57
Timestamp: 2025-04-11T08:39:18.375Z
Learning: For Zapier integration redirect URI validation, implement domain whitelisting to prevent open redirect vulnerabilities. Check incoming URIs against a predefined list of trusted domains using URL parsing before generating auth codes.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts:319-349
Timestamp: 2025-04-15T21:47:59.877Z
Learning: The Zapier integration currently uses in-memory locks for concurrency control, with plans to implement Redis-based distributed locking in the future. This is a known limitation that should not be flagged in reviews.
🧬 Code Graph Analysis (2)
packages/config/src/lib/config/zapier.ts (1)
packages/common/src/lib/interfaces/IZapierConfig.ts (1)
IZapierConfig
(4-25)
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (1)
packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)
🪛 GitHub Actions: Check Spelling and Typos with cspell
packages/config/src/lib/environments/ienvironment.ts
[warning] 20-113: Multiple unknown words 'Posthog' and 'posthog' found by cspell.
.env.compose
[warning] 259-262: Unknown word 'POSTHOG' found by cspell.
.env.sample
[warning] 249-252: Unknown word 'POSTHOG' found by cspell.
packages/config/src/lib/environments/environment.prod.ts
[warning] 174-178: Multiple unknown words 'posthog' and 'POSTHOG' found by cspell.
.env.demo.compose
[warning] 266-269: Unknown word 'POSTHOG' found by cspell.
packages/config/src/lib/environments/environment.ts
[warning] 174-178: Multiple unknown words 'posthog' and 'POSTHOG' found by cspell.
.env.docker
[warning] 245-248: Unknown word 'POSTHOG' found by cspell.
.env.local
[warning] 250-253: Unknown word 'POSTHOG' found by cspell.
🪛 Biome (1.9.4)
packages/config/src/lib/environments/environment.prod.ts
[error] 207-207: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (9)
packages/config/src/lib/environments/ienvironment.ts (1)
20-21
: Import order adjustment looks good.The reordering of the imports (IPosthogConfig before IZapierConfig) maintains alphabetical order and follows good import organization practices.
🧰 Tools
🪛 GitHub Actions: Check Spelling and Typos with cspell
[warning] 20-113: Multiple unknown words 'Posthog' and 'posthog' found by cspell.
.env.compose (1)
153-156
: Good security practice with Zapier OAuth configuration.The new environment variables add important security controls for the Zapier OAuth integration:
- Domain allowlist restricts OAuth redirects to trusted domains
- Max auth codes limit prevents potential memory issues
- Instance count supports proper cleanup in different deployment scenarios
All values are properly set and well-commented.
packages/config/src/lib/config/index.ts (1)
11-11
: Good integration of Zapier configuration.The Zapier module is properly imported and added to the configuration array, maintaining the existing pattern for integration configurations.
Also applies to: 16-16
.env.demo.compose (1)
154-157
: Comprehensive OAuth security configuration added.These new environment variables enhance the Zapier integration security and performance by:
- Restricting OAuth redirects to authorized domains only
- Limiting the number of stored authorization codes
- Configuring instance count for distributed deployments
These additions align perfectly with OAuth best practices by implementing domain validation and resource limits.
packages/config/src/lib/environments/environment.ts (1)
203-207
: Well-implemented configuration parsing with proper error handling.The implementation correctly:
- Parses and trims domain values from environment variables
- Uses
Number.parseInt
for numeric conversion- Provides sensible defaults (empty array, 1000 codes)
- Properly handles boolean conversion for instance count
This clean implementation ensures robust configuration handling for the enhanced OAuth flow.
.env.local (1)
168-171
: Include localhost in allowed domains for local development.The
GAUZY_ZAPIER_ALLOWED_DOMAINS
list only includes production domains, which may prevent OAuth redirects from working during local development and testing.Add localhost to the allowed domains list:
-GAUZY_ZAPIER_ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com +GAUZY_ZAPIER_ALLOWED_DOMAINS=gauzy.co,ever.co,staging.gauzy.co,demo.gauzy.co,zapier.com,github.com,upwork.com,hubstaff.com,fiverr.com,localhost:3000,localhost:4200This will ensure that OAuth redirects work properly in local development environments.
packages/config/src/lib/config/zapier.ts (1)
19-21
:instanceCount
representation differs across configsHere
instanceCount
is parsed to a number, butenvironment.prod.ts
stores a boolean, andZapierAuthCodeService
expects either a boolean‑string or number.
Pick one canonical type and stick to it, otherwise run‑time checks (trim
, arithmetic) will fail unpredictably.packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (1)
85-88
: Verify ConfigService path forbaseUrl
this._config.get('baseUrl')
assumesbaseUrl
is registered at the root level.
If the value lives under another namespace (e.g.environment.baseUrl
) the call returnsundefined
, producing confusing logs.Confirm the path or adjust the key accordingly.
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (1)
299-303
: Token type casingSome clients expect
token_type
to be lowercase (bearer
). Confirm Zapier’s expectation and adjust if necessary:- token_type: 'Bearer' + token_type: 'bearer'
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
Outdated
Show resolved
Hide resolved
this.logger.debug('OAuth authorize request received'); | ||
this.logger.debug(`Redirect URI: ${redirectUri}`); | ||
this.logger.debug(`State: ${state}`); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Avoid logging sensitive state
values
The state
parameter is a CSRF token. Persisting it verbatim in logs can aid replay attacks if log access is compromised. Mask it similarly to how you already obfuscate code
.
- this.logger.debug(`State: ${state}`);
+ this.logger.debug(`State: ***`);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this.logger.debug('OAuth authorize request received'); | |
this.logger.debug(`Redirect URI: ${redirectUri}`); | |
this.logger.debug(`State: ${state}`); | |
this.logger.debug('OAuth authorize request received'); | |
this.logger.debug(`Redirect URI: ${redirectUri}`); | |
this.logger.debug(`State: ***`); |
// Validate required parameters | ||
if (!state) { | ||
throw new HttpException('Missing state parameter', HttpStatus.BAD_REQUEST); | ||
} | ||
|
||
// Validate the redirect URI against allowed domains | ||
this.validateRedirectDomain(zapier_redirect_uri); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add explicit check for missing zapier_redirect_uri
in callback
validateRedirectDomain
will currently throw “Invalid redirect URI format” if the param is absent, which is confusing. Perform a null/empty check first for clearer feedback:
+ if (!zapier_redirect_uri) {
+ throw new BadRequestException('Missing zapier_redirect_uri parameter');
+ }
this.validateRedirectDomain(zapier_redirect_uri);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Validate required parameters | |
if (!state) { | |
throw new HttpException('Missing state parameter', HttpStatus.BAD_REQUEST); | |
} | |
// Validate the redirect URI against allowed domains | |
this.validateRedirectDomain(zapier_redirect_uri); | |
// Validate required parameters | |
if (!state) { | |
throw new HttpException('Missing state parameter', HttpStatus.BAD_REQUEST); | |
} | |
// Validate the redirect URI against allowed domains | |
+ if (!zapier_redirect_uri) { | |
+ throw new BadRequestException('Missing zapier_redirect_uri parameter'); | |
+ } | |
this.validateRedirectDomain(zapier_redirect_uri); |
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
Show resolved
Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
♻️ Duplicate comments (1)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (1)
33-37
:⚠️ Potential issue
allowedDomains
type mismatch –.split(',')
will crash when config already provides an array
ConfigService#get
returns the exact value stored inenvironment.*.ts
.
Becauseenvironment.prod.ts
now setsallowedDomains
asstring[]
, calling.split
on an array will throwTypeError: configDomains.split is not a function
.- const configDomains = this._config.get<string>('zapier.allowedDomains'); - this.ALLOWED_DOMAINS = configDomains - ? Array.from(new Set([...defaultDomains, ...configDomains.split(',').map((domain) => domain.trim())])) - : defaultDomains; + const configDomains = this._config.get<string | string[]>('zapier.allowedDomains') ?? []; + const domainsArray = Array.isArray(configDomains) + ? configDomains.map((d) => d.trim()) + : configDomains.split(',').map((d) => d.trim()); + + this.ALLOWED_DOMAINS = Array.from(new Set([...defaultDomains, ...domainsArray]));Without this guard the service will fail on startup in production.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
packages/config/src/lib/environments/environment.prod.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.service.ts
(7 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (5)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:27-29
Timestamp: 2025-04-15T18:29:09.314Z
Learning: The ZapierAuthCodeService in the Zapier integration plugin is designed to be initialized only once as a singleton service in the NestJS application.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:6-19
Timestamp: 2025-04-11T06:13:47.856Z
Learning: For the Zapier integration auth code storage, using an in-memory Map is acceptable for moderate usage volumes due to the built-in safeguards: a maximum limit of 1000 codes and an automatic cleanup mechanism for expired codes.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:93-105
Timestamp: 2025-04-11T09:03:32.978Z
Learning: For the Zapier integration auth code storage, on-demand cleanup (triggered when approaching the maximum code limit) is sufficient without the need for periodic scheduled cleanup, as determined by the implementation requirements.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:37-70
Timestamp: 2025-04-11T14:06:04.523Z
Learning: The ZapierAuthCodeService unit tests will be implemented in a future PR, as confirmed by RolandM99.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:145-165
Timestamp: 2025-04-15T13:43:30.444Z
Learning: The domain validation in ZapierAuthCodeService doesn't need to handle IP addresses, as OAuth redirects typically only require domain name validation.
🧬 Code Graph Analysis (1)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (5)
packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-stopped.handler.ts (1)
Injectable
(6-31)packages/plugins/integration-zapier/src/lib/handlers/zapier-timer-started.handler.ts (1)
Injectable
(6-30)packages/plugins/integration-zapier/src/lib/repository/type-orm-zapier.repository.ts (1)
Injectable
(6-13)packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)packages/contracts/src/lib/zapier.model.ts (1)
ITimerZapierWebhookData
(23-27)
🪛 Biome (1.9.4)
packages/config/src/lib/environments/environment.prod.ts
[error] 208-208: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
[error] 257-258: Expected a statement but instead found '}'.
Expected a statement here.
(parse)
🪛 GitHub Actions: Check Spelling and Typos with cspell
packages/config/src/lib/environments/environment.prod.ts
[warning] 174-178: cspell: Multiple unknown words 'posthog' and 'POSTHOG' found in this file.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (2)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (1)
80-88
:MAX_AUTH_CODES
back‑pressure is synchronous – consider early 429Cleaning up then throwing a
503 ServiceUnavailable
may not convey the throttling nature of the limit.
ATooManyRequestsException(429)
better matches client expectations and eases retry/back‑off handling.Would switching to
TooManyRequestsException
break any existing client logic?packages/plugins/integration-zapier/src/lib/zapier.service.ts (1)
112-117
:⚠️ Potential issueLeaking sensitive data to logs
client_id
is logged at the info level. This is an OAuth credential and should be treated as secret. Strip it or mask it:- client_id, + // client_id intentionally omitted for securityLikely an incorrect or invalid review comment.
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
Outdated
Show resolved
Hide resolved
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
Outdated
Show resolved
Hide resolved
async createIntegration(input: ICreateZapierIntegrationInput & { userId: string }): Promise<IZapierAccessTokens> { | ||
const { client_id, client_secret, organizationId, tenantId, userId } = input; | ||
|
||
const urlParams = new URLSearchParams(); | ||
urlParams.append('client_id', client_id); | ||
urlParams.append('code', code); | ||
urlParams.append('grant_type', 'authorization_code'); | ||
urlParams.append('redirect_uri', redirect_uri); | ||
urlParams.append('client_secret', client_secret); | ||
// Generate access token and refresh token | ||
const access_token = uuidv4(); | ||
const refresh_token = uuidv4(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate mandatory OAuth fields at entry‑point
client_id
and client_secret
are persisted without null‑checks.
If the UI omits them, the DB will store empty strings, causing failures later in the flow. Add validation before generating tokens.
if (!client_id || !client_secret) {
throw new BadRequestException('client_id and client_secret are required');
}
settingsName: 'client_id', | ||
settingsValue: client_id | ||
}, | ||
{ | ||
settingsName: 'client_secret', | ||
settingsValue: client_secret | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Store client_secret
encrypted or hashed
The secret is persisted in plaintext. Consider encrypting at rest (e.g., using TypeORM’s EncryptTransformer
or a KMS) or at least base‑64 encoding plus environment‑level encryption.
Persisting raw secrets violates the principle of least exposure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai Show me example how to encrypt and during retrive decrypt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rahul-rocket Here are two approaches to encrypt sensitive data like client_secret
:
1. Using TypeORM's Column Transformer
// In your entity file (e.g., integration-setting.entity.ts)
import { Column, Entity } from 'typeorm';
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
// Custom transformer for encryption/decryption
export class EncryptionTransformer {
private encryptionKey: Buffer;
private algorithm: string;
constructor(options: { key: string; algorithm?: string }) {
this.encryptionKey = Buffer.from(options.key, 'hex');
this.algorithm = options.algorithm || 'aes-256-cbc';
}
to(value: string): string {
if (!value) return value;
const iv = randomBytes(16);
const cipher = createCipheriv(this.algorithm, this.encryptionKey, iv);
let encrypted = cipher.update(value, 'utf8', 'hex');
encrypted += cipher.final('hex');
return Buffer.from(iv.toString('hex') + encrypted).toString('base64');
}
from(value: string): string {
if (!value) return value;
const buff = Buffer.from(value, 'base64');
const stringValue = buff.toString('utf8');
const iv = Buffer.from(stringValue.slice(0, 32), 'hex');
const encrypted = stringValue.slice(32);
const decipher = createDecipheriv(this.algorithm, this.encryptionKey, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
@Entity('integration_setting')
export class IntegrationSetting {
@Column({
name: 'settings_value',
transformer: new EncryptionTransformer({
key: process.env.ENCRYPTION_KEY || '1234567890abcdef1234567890abcdef' // Use env var in production
})
})
settingsValue: string;
}
2. Manual Service-based Approach
// Create an encryption service
@Injectable()
export class EncryptionService {
private readonly encryptionKey: Buffer;
constructor() {
this.encryptionKey = Buffer.from(
process.env.ENCRYPTION_KEY || '1234567890abcdef1234567890abcdef',
'hex'
);
}
encrypt(text: string): string {
const iv = randomBytes(16);
const cipher = createCipheriv('aes-256-cbc', this.encryptionKey, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return Buffer.from(iv.toString('hex') + encrypted).toString('base64');
}
decrypt(encryptedText: string): string {
const buff = Buffer.from(encryptedText, 'base64');
const stringValue = buff.toString('utf8');
const iv = Buffer.from(stringValue.slice(0, 32), 'hex');
const encrypted = stringValue.slice(32);
const decipher = createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// Then in your ZapierService:
async createIntegration(input: ICreateZapierIntegrationInput & { userId: string }): Promise<IZapierAccessTokens> {
// ...existing code...
const encryptedClientSecret = this.encryptionService.encrypt(client_secret);
// Use encrypted value in settings
settings: [
// ...
{
settingsName: 'client_secret',
settingsValue: encryptedClientSecret
}
// ...
]
// ...rest of method...
}
// When retrieving:
const encryptedSecret = settings.find(s => s.settingsName === 'client_secret')?.settingsValue;
const decryptedSecret = this.encryptionService.decrypt(encryptedSecret);
Important notes:
- Store encryption keys in environment variables, never hardcode in production
- Consider key rotation for long-term security
- Add proper error handling for encryption/decryption failures
✏️ Learnings added
Learnt from: rahul-rocket
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier.service.ts:213-219
Timestamp: 2025-04-21T12:41:32.207Z
Learning: When storing sensitive data like OAuth client secrets in the database, encrypt them using Node.js crypto module with proper IV handling and store in base64 format. Use either TypeORM column transformers for automatic encryption/decryption or a dedicated encryption service.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
🔭 Outside diff range comments (3)
packages/plugins/integration-zapier/src/lib/zapier-webhook.controller.ts (2)
60-70
: 🛠️ Refactor suggestionHarden the Authorization header parsing
authorization.startsWith('Bearer ')
will fail whenever the header contains leading/trailing spaces or if the scheme is sent in a different case (e.g. “bearer”). Consider normalising and trimming first:- if (!authorization.startsWith('Bearer ')) { + const trimmed = authorization.trim() + if (!trimmed.toLowerCase().startsWith('bearer ')) {This prevents false negatives from otherwise valid requests.
60-61
: 🛠️ Refactor suggestionDTO lacks runtime validation
@Body() body: IZapierCreateWebhookInput
is injected without aValidationPipe
. Any missing/extra fields or wrong types will reach the service layer unchecked.
Register a globalValidationPipe
or decorate the route with@UsePipes(new ValidationPipe({ whitelist: true }))
.Also applies to: 96-104
packages/plugins/integration-zapier/src/lib/zapier.service.ts (1)
44-56
:⚠️ Potential issue
fetchIntegration
uses relative URLsPassing plain strings like
'triggers'
or'actions'
toHttpService.get()
yields requests tohttp://localhost/triggers
by default, which will 404 in production.
Accept a full URL or prepend the Zapier base URL from config:- this._httpService.get(url, { headers }) + this._httpService.get(`${this.zapierBaseUrl}/${url}`, { headers })
♻️ Duplicate comments (15)
packages/config/src/lib/config/zapier.ts (1)
23-26
: 🧹 Nitpick (assertive)Minor: flatten the domain pipeline (matches previous nit‑pick)
For symmetry with
environment.prod.ts
, you can collapse the expression:- allowedDomains: (process.env.GAUZY_ZAPIER_ALLOWED_DOMAINS ?? '') - .split(',') - .map((d) => d.trim()) - .filter(Boolean), + allowedDomains: (process.env.GAUZY_ZAPIER_ALLOWED_DOMAINS ?? '') + .split(',') + .map((d) => d.trim()) + .filter(Boolean),(identical logic, just emphasises single straight‑line flow)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (1)
137-154
: 🧹 Nitpick (assertive)Bounded parallel dispatch & timeout will harden webhook notifications
Promise.all
fires requests in parallel – good – but a hung endpoint can stall the wholefirstValueFrom
for up to the default Axios timeout (no explicit limit set). Consider:- await Promise.all( + await Promise.allSettled( subscriptions.map((sub) => firstValueFrom( this._httpService - .post(sub.targetUrl, { + .post( + sub.targetUrl, + { event: 'timer.status.changed', data: timerData }, + { timeout: 5000 } // 5 s guard rail + ) + .pipe( + catchError((err) => { + this.logger.error( + `Failed to notify webhook ${sub.id} at ${sub.targetUrl}`, + err?.stack + ); + return of(null); + }) + ) + ) - }) - .pipe( - catchError((err) => { - this.logger.error(`Failed to notify webhook ${sub.id} at ${sub.targetUrl}`, err); - return of(null); // swallow error so other calls continue - }) - ) - ) ) ) );Benefits:
•Promise.allSettled
prevents one rejected promise from aborting the whole notification round.
• Short timeout avoids long event‑loop blockage on slow/non‑responsive endpoints.packages/plugins/integration-zapier/src/lib/zapier.service.ts (2)
92-107
: Tokens are only updated when rows already existThe refresh routine mutates existing rows but does not insert missing
access_token
/refresh_token
settings (earlier review). First‑time integrations still lose the new tokens.
Reuse the “push‑fallback” snippet suggested previously or use an upsert.
196-238
: Store client secrets encrypted at rest
client_secret
is persisted in plain text. Encrypt with a TypeORM transformer or KMS to minimise breach impact.packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (2)
184-192
: Clear interval on application shutdown
setInterval
is created but never cleared. In hot‑reload/dev or test environments this leaks timers.
ImplementOnApplicationShutdown
and clear the handle:private cleanupHandle?: NodeJS.Timer; … this.cleanupHandle = setInterval(…) … onApplicationShutdown() { if (this.cleanupHandle) clearInterval(this.cleanupHandle); }
64-70
: Potential race when reaching MAX_AUTH_CODES
ensureAuthCodeCapacity()
deletes expired codes but two concurrent requests can still overshoot the limit.
Single‑threaded Node mitigates this somewhat, yet a mutex (e.g.async-mutex
) around capacity checks was previously suggested.Also applies to: 107-118
packages/plugins/integration-zapier/src/lib/zapier.controller.ts (1)
159-167
: Mixed 400 vs 401 semantics
validateToken
decides betweenBadRequestException
andUnauthorizedException
based on a boolean flag. This is non‑obvious and was raised before.
Instead expose two explicit helpers (assertTokenProvided
,assertTokenAuthorized
) or always throwUnauthorizedException
to simplify reasoning.packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (8)
83-86
: Avoid logging rawstate
values
state
works as a CSRF token; exposing it verbatim can aid replay attacks if logs leak. Mask it the same way you already maskcode
.
100-101
: Generic error message for credential mismatch
Returning “Invalid client ID” discloses which piece of the credential pair was wrong. Use a neutral message like “Invalid client credentials”.
218-220
: UseZapierGrantType
enum instead of string literal
Comparing to the hard‑coded string ('authorization_code'
) is error‑prone; prefer the enum for type safety.
229-231
: Neutralise credential‑mismatch message
Same issue as above – reveal nothing about which field failed.
234-243
: Auth codes should be one‑time use
After a successful token exchange the code remains valid, enabling replay attacks. Invalidate it immediately after tokens are issued:const userInfo = this.zapierAuthCodeService.getUserInfoFromAuthCode(code); ... const tokens = await this.zapierService.createIntegration({ ... }); +this.zapierAuthCodeService.invalidateAuthCode(code);
318-324
: Avoid logging client credentials
client_id
and especiallyclient_secret
appear in DEBUG logs. Redact them before logging to prevent accidental leakage.
327-330
: Use enum instead of string literal for refresh grant type
Same enum‑safety concern as ingetToken
.
34-41
:⚠️ Potential issue
allowedDomains
is never populated – all requests will fail domain validation
allowedDomains
is initialised as an empty array and never assigned, sovalidateRedirectDomain()
will always reject a URI (includes()
will return false) and the flow will break for every request.private allowedDomains: string[] = []; constructor( private readonly _config: ConfigService, private readonly zapierAuthCodeService: ZapierAuthCodeService, private readonly zapierService: ZapierService ) {} + +onModuleInit() { + const raw = this._config.get<string>('zapier.allowedDomains') ?? ''; + this.allowedDomains = Array.from( + new Set( + raw + .split(',') + .map((d) => d.trim().toLowerCase()) + .filter(Boolean) + ) + ); + + if (this.allowedDomains.length === 0) { + this.logger.warn( + 'No allowed domains configured for Zapier integration – all redirect URIs will be rejected' + ); + } +}Without this fix, no one can complete the OAuth flow.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (20)
.env.compose
(1 hunks).env.docker
(1 hunks).scripts/icon-utils/icon-factory.ts
(1 hunks)packages/config/src/lib/config/zapier.ts
(1 hunks)packages/config/src/lib/environments/environment.prod.ts
(1 hunks)packages/config/src/lib/environments/environment.ts
(1 hunks)packages/contracts/src/index.ts
(0 hunks)packages/contracts/src/lib/zapier.model.ts
(0 hunks)packages/plugins/integration-zapier/src/lib/dto/create-zapier-integration.dto.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
(3 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook-subscription.entity.ts
(2 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook.controller.ts
(5 hunks)packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.config.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.controller.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.module.ts
(1 hunks)packages/plugins/integration-zapier/src/lib/zapier.service.ts
(6 hunks)packages/plugins/integration-zapier/src/lib/zapier.types.ts
(1 hunks)
💤 Files with no reviewable changes (2)
- packages/contracts/src/index.ts
- packages/contracts/src/lib/zapier.model.ts
🧰 Additional context used
🧠 Learnings (3)
packages/plugins/integration-zapier/src/lib/zapier.config.ts (1)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8863
File: packages/plugins/integration-zapier/zapier/src/authentication.ts:29-29
Timestamp: 2025-04-02T09:39:39.648Z
Learning: When developing Zapier integrations that will be deployed to the Zapier platform, use `process.env` directly to access environment variables instead of importing from `@gauzy/config`, as Zapier's platform cannot recognize or import this package.
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (5)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:27-29
Timestamp: 2025-04-15T18:29:09.314Z
Learning: The ZapierAuthCodeService in the Zapier integration plugin is designed to be initialized only once as a singleton service in the NestJS application.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:6-19
Timestamp: 2025-04-11T06:13:47.856Z
Learning: For the Zapier integration auth code storage, using an in-memory Map is acceptable for moderate usage volumes due to the built-in safeguards: a maximum limit of 1000 codes and an automatic cleanup mechanism for expired codes.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:93-105
Timestamp: 2025-04-11T09:03:32.978Z
Learning: For the Zapier integration auth code storage, on-demand cleanup (triggered when approaching the maximum code limit) is sufficient without the need for periodic scheduled cleanup, as determined by the implementation requirements.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:37-70
Timestamp: 2025-04-11T14:06:04.523Z
Learning: The ZapierAuthCodeService unit tests will be implemented in a future PR, as confirmed by RolandM99.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:145-165
Timestamp: 2025-04-15T13:43:30.444Z
Learning: The domain validation in ZapierAuthCodeService doesn't need to handle IP addresses, as OAuth redirects typically only require domain name validation.
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (2)
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts:21-57
Timestamp: 2025-04-11T08:39:18.375Z
Learning: For Zapier integration redirect URI validation, implement domain whitelisting to prevent open redirect vulnerabilities. Check incoming URIs against a predefined list of trusted domains using URL parsing before generating auth codes.
Learnt from: RolandM99
PR: ever-co/ever-gauzy#8901
File: packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts:319-349
Timestamp: 2025-04-15T21:47:59.877Z
Learning: The Zapier integration currently uses in-memory locks for concurrency control, with plans to implement Redis-based distributed locking in the future. This is a known limitation that should not be flagged in reviews.
🧬 Code Graph Analysis (5)
packages/config/src/lib/config/zapier.ts (1)
packages/common/src/lib/interfaces/IZapierConfig.ts (1)
IZapierConfig
(4-25)
packages/plugins/integration-zapier/src/lib/zapier.module.ts (4)
packages/config/src/lib/environments/environment.ts (1)
environment
(18-281)packages/core/src/index.ts (8)
IntegrationEntitySettingModule
(76-76)IntegrationMapModule
(55-55)IntegrationModule
(47-47)RolePermissionModule
(23-23)IntegrationSettingModule
(69-69)IntegrationTenantModule
(49-49)UserModule
(24-24)TimerModule
(96-96)packages/plugins/integration-zapier/src/lib/repository/mikro-orm-zapier-webhook-subscription.repository.ts (1)
MikroOrmZapierWebhookSubscriptionRepository
(4-4)packages/plugins/integration-zapier/src/lib/handlers/index.ts (1)
EventHandlers
(4-4)
packages/plugins/integration-zapier/src/lib/zapier.types.ts (2)
packages/contracts/src/index.ts (1)
IBasePerTenantAndOrganizationEntityModel
(158-158)packages/contracts/src/lib/timesheet.model.ts (1)
ITimeLog
(99-126)
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (1)
packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (3)
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts (1)
Injectable
(15-156)packages/desktop-core/src/lib/logger/logger.ts (1)
Logger
(5-32)packages/contracts/src/index.ts (1)
ID
(162-162)
🪛 Biome (1.9.4)
packages/plugins/integration-zapier/src/lib/zapier.config.ts
[error] 19-19: Template literals are preferred over string concatenation.
Unsafe fix: Use a template literal.
(lint/style/useTemplate)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: test
- GitHub Check: build
🔇 Additional comments (19)
.scripts/icon-utils/icon-factory.ts (1)
2-2
: Load environment variables as early as possible.
Movingdotenv.config()
immediately after importingdotenv
ensures that any subsequent module imports or code executions that depend on environment variables will have access to them. This aligns with best practices for configuration management.packages/plugins/integration-zapier/src/lib/dto/create-zapier-integration.dto.ts (1)
4-4
: Import path refactoring looks good.The change from an external package import to a local module import for Zapier types centralizes the integration-specific types within the plugin, which is good for maintainability.
.env.compose (1)
152-155
: Enhanced OAuth security with proper domain restrictions.The addition of these environment variables significantly improves the security posture of the Zapier integration:
GAUZY_ZAPIER_ALLOWED_DOMAINS
restricts OAuth redirects to a trusted list of domains, preventing open redirect vulnerabilitiesGAUZY_ZAPIER_MAX_AUTH_CODES
prevents potential resource exhaustion attacksGAUZY_ZAPIER_INSTANCE_COUNT
supports proper cleanup in multi-instance deploymentsThese changes align with OAuth security best practices.
packages/plugins/integration-zapier/src/lib/zapier.config.ts (2)
7-8
: Dynamic callback URL configuration looks good.Constructing the callback URL dynamically from the environment is a good practice for flexibility across different deployment environments.
16-17
: Explicit token expiration time is a security best practice.Setting a fixed expiration time for OAuth tokens (1 hour) is good for security. This follows OAuth best practices by limiting the lifetime of access tokens.
packages/config/src/lib/environments/environment.ts (1)
203-208
: Well-implemented security enhancements for OAuth.The implementation of domain restrictions and auth code limits shows good attention to security and performance:
- The domain parsing includes fallbacks, empty string filtering, and trimming
- Using
Number.parseInt
with a default value formaxAuthCodes
ensures a valid number- The
instanceCount
flag enables proper management in multi-instance deploymentsThese changes align with the retrieved learning about environment variables in Zapier integrations.
.env.docker (1)
151-154
: Security and configuration parameters for OAuth integration look good.The newly added environment variables provide important security controls for the Zapier OAuth integration:
- Domain allowlist for restricting OAuth redirects to trusted domains
- Memory limits for authentication codes
- Instance count parameter for controlling cleanup processes
packages/plugins/integration-zapier/src/lib/zapier-webhook-subscription.entity.ts (2)
14-14
: Repository import path correctly updated.The import path has been fixed to properly reference the MikroORM repository with the full name.
31-56
: Good improvements to entity relationship documentation and structure.The changes improve code maintainability through:
- Clear documentation of the ManyToOne relationship
- Making the integration relationship optional with
?
syntax- Adding proper JSDoc comments
- Explicitly defining the relationId column with appropriate decorators
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts (4)
1-3
: Improved imports with proper dependency management.Replacing direct chalk usage with NestJS Logger and adding ConfigService for configuration management follows best practices.
75-78
: Great improvement using NestJS Logger and dependency injection.Replacing the custom logging flag with NestJS's built-in Logger improves consistency with the framework patterns. The ConfigService injection enables proper configuration management.
83-95
: Enhanced bootstrap process with proper configuration validation.The improved initialization process:
- Uses structured logging
- Retrieves credentials via ConfigService
- Validates OAuth configuration
- Provides helpful warning messages when configuration is incomplete
102-102
: Consistent logging for plugin lifecycle events.Using the logger instance for the destroy event maintains consistency throughout the plugin lifecycle.
packages/plugins/integration-zapier/src/lib/zapier.module.ts (3)
8-16
: Improved module imports organization.The imports from '@gauzy/core' have been properly organized for better readability and maintenance.
23-26
: Updated repository paths and added new service import.The repository import paths now correctly include the full names, and the new imports support the shift to an event-driven architecture.
29-43
: Well-structured module imports with consistent formatting.The imports array is now properly formatted with consistent indentation, improving code readability.
packages/config/src/lib/environments/environment.prod.ts (1)
207-208
: InconsistentGAUZY_ZAPIER_INSTANCE_COUNT
interpretation across configsHere the variable is parsed to boolean (
=== 'true'
),
whereaspackages/config/src/lib/config/zapier.ts
parses it to number (Number.parseInt(...) || 1
).
Down‑stream services reading from both providers will see different types, risking subtle bugs.Actionable suggestions:
- Decide on one representation (boolean flag or numeric workers) and keep both files aligned.
- Add runtime validation (e.g.,
class‑validator
) to detect mismatches early.packages/plugins/integration-zapier/src/lib/zapier.types.ts (1)
1-37
: Good move localising Zapier typesPlacing Zapier‑specific DTOs inside the plugin improves modularity and reduces cross‑package coupling.
Interfaces closely mirror Zapier’s payloads, justifying snake_case property names. No issues spotted. 👍packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts (1)
205-220
:⚠️ Potential issueSub‑domain validation is overly permissive
isSubdomain()
matches any domain ending inallowedDomain
, soevil.example.com.fake
passes forexample.com
.
Limit to exact sub‑domain depth:return ( domainParts.length === allowedParts.length + 1 && // one extra label only allowedParts.every((p, i) => domainParts[i] === p) );or leverage the public‑suffix list via
tldts
for robust checks.Likely an incorrect or invalid review comment.
controllers: [ZapierAuthorizationController, ZapierController, ZapierWebhookController], | ||
providers: [ | ||
ZapierService, | ||
ZapierWebhookService, | ||
ZapierAuthCodeService, | ||
MikroOrmZapierWebhookSubscriptionRepository, | ||
TypeOrmZapierWebhookSubscriptionRepository, | ||
...EventHandlers | ||
], | ||
exports: [ZapierService, ZapierWebhookService, ZapierAuthCodeService] | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Transition to event-driven architecture and addition of auth code service.
The module now:
- Uses event handlers instead of command/query handlers
- Includes the new ZapierAuthCodeService for OAuth code management
- Correctly exports the necessary services
Note: TimerModule is still imported but not exported - verify it's needed.
🏁 Script executed:
#!/bin/bash
# Check if TimerModule is used within the Zapier module's components
# Look for TimerModule usage in event handlers
echo "Checking for TimerModule usage in event handlers:"
fd --type f "zapier-.*handler\.ts" packages/plugins/integration-zapier/src/lib/handlers/ --exec grep -l "TimerModule\|TimerService" {} \;
# Check if any other components import Timer-related services
echo "Checking for Timer-related imports in other components:"
fd --type f ".*\.ts" packages/plugins/integration-zapier/src/lib/ --exec grep -l "TimerService\|TimerEntity" {} \;
Length of output: 492
🏁 Script executed:
#!/bin/bash
echo "Inspecting zapier.module.ts for TimerModule import and usage"
grep -R -n "TimerModule" packages/plugins/integration-zapier/src/lib/zapier.module.ts || echo "No TimerModule import found"
echo -e "\nShowing the @Module decorator block in zapier.module.ts (first 200 lines):"
sed -n '1,200p' packages/plugins/integration-zapier/src/lib/zapier.module.ts
Length of output: 2561
Remove unused TimerModule from ZapierModule
It looks like TimerModule is imported in packages/plugins/integration-zapier/src/lib/zapier.module.ts but none of the controllers, services, or event handlers reference TimerModule or TimerService. Unless there’s an external dependency not covered in this code, it can be safely removed to avoid unused imports.
• File: packages/plugins/integration-zapier/src/lib/zapier.module.ts
– In the imports:
array, remove the TimerModule
entry.
instanceCount: Number.parseInt(process.env.GAUZY_ZAPIER_INSTANCE_COUNT) || 1, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
instanceCount
fallback forces “1”, ignores legitimate “0”
Number.parseInt(process.env.GAUZY_ZAPIER_INSTANCE_COUNT) || 1
returns 1
for an env‑var of "0"
or any non‑numeric value (NaN).
If “0” is meant to disable the cleaner, this logic overrides it.
- instanceCount: Number.parseInt(process.env.GAUZY_ZAPIER_INSTANCE_COUNT) || 1,
+ const _raw = Number.parseInt(process.env.GAUZY_ZAPIER_INSTANCE_COUNT);
+ instanceCount: Number.isNaN(_raw) ? 1 : _raw,
(or keep it optional and default to undefined
)
Committable suggestion skipped: line range outside the PR's diff.
} catch (error) { | ||
this.logger.error('Failed to create webhook subscription', error); | ||
throw new InternalServerErrorException('Failed to create webhook subscription'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Logger.error
second argument should be a string stack
Logger.error(message, stack?)
expects the second arg to be a string, not an Error
object.
Passing the raw error object prints [object Object]
. Use error?.stack
(falls back to error?.message
) for clearer logs.
- this.logger.error('Failed to create webhook subscription', error);
+ this.logger.error('Failed to create webhook subscription', error?.stack);
(and similarly in other .error
calls)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
} catch (error) { | |
this.logger.error('Failed to create webhook subscription', error); | |
throw new InternalServerErrorException('Failed to create webhook subscription'); | |
} | |
} catch (error) { | |
this.logger.error('Failed to create webhook subscription', error?.stack); | |
throw new InternalServerErrorException('Failed to create webhook subscription'); | |
} |
packages/plugins/integration-zapier/src/lib/zapier-webhook.controller.ts
Show resolved
Hide resolved
const access_token = uuidv4(); | ||
const new_refresh_token = uuidv4(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider cryptographically‑strong tokens
uuidv4()
is convenient, but OAuth access/refresh tokens should be unpredictable and uniformly random.
crypto.randomBytes(32).toString('hex')
offers 256‑bit entropy and is still URL‑safe.
@Get('/triggers') | ||
async getTriggers(@Query('token') token: string): Promise<IZapierEndpoint[]> { | ||
try { | ||
const token = await this.zapierService.refreshToken(integrationId); | ||
if (!token) { | ||
// If service returns null/undefined, assume invalid credentials | ||
throw new UnauthorizedException(`Failed to refresh token - invalid credentials`); | ||
} | ||
return token; | ||
this.validateToken(token, true); | ||
return await this.zapierService.fetchTriggers(token); | ||
} catch (error) { | ||
this.logger.error(`Failed to refresh Zapier token for integration ${integrationId}`, error); | ||
|
||
// Re-throw specific errors | ||
if (error instanceof NotFoundException || error instanceof UnauthorizedException) { | ||
throw error; | ||
} | ||
|
||
// For unexpected errors, throw internal server error | ||
throw new InternalServerErrorException('Failed to refresh Zapier integration token'); | ||
this.handleZapierError(error, 'triggers'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Early token format validation but not existence validation
validateToken
only checks presence/emptiness. An attacker can pass any random string and still reach fetchTriggers/Actions
, which will likely 500.
Call zapierService.findIntegrationByToken(token)
first and throw UnauthorizedException
when not found to provide a clean 401.
Also applies to: 147-153
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts
Outdated
Show resolved
Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…er-gauzy into fix/zapier-plugin-integration
PR
Please note: we will close your PR without comment if you do not check the boxes above and provide ALL requested information.