Skip to content

[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

Merged
merged 37 commits into from
Apr 21, 2025

Conversation

RolandM99
Copy link
Contributor

@RolandM99 RolandM99 commented Apr 7, 2025

PR

Please note: we will close your PR without comment if you do not check the boxes above and provide ALL requested information.


Copy link
Contributor

@greptile-apps greptile-apps bot left a 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

Copy link
Contributor

coderabbitai bot commented Apr 7, 2025

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b9efa5 and d7ca3da.

📒 Files selected for processing (9)
  • .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/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.config.ts (1 hunks)

Summary by CodeRabbit

  • New Features

    • Introduced full OAuth2 authorization code flow for Zapier integration, including secure token issuance, refresh, and domain validation.
    • Added dynamic dropdowns for tenants, organizations, projects, tasks, contacts, and teams in Zapier actions.
    • Implemented new Zapier triggers and actions for enhanced automation and integration capabilities.
    • Added timer event webhooks to notify Zapier on timer start/stop.
  • Improvements

    • Enhanced error handling, logging, and validation across Zapier OAuth endpoints and services.
    • Improved user experience with dynamic input fields, connection labels, and descriptive help texts in Zapier app actions.
    • Updated environment and configuration to support allowed domains and auth code limits for OAuth security.
  • Bug Fixes

    • Fixed authorization header handling and improved input validation in Zapier webhook and timer endpoints.
  • Chores

    • Updated dependencies for Zapier platform libraries.
    • Added .tsbuildinfo to .gitignore for cleaner builds.
  • Documentation

    • Improved API documentation and help texts for Zapier integration endpoints and actions.

Summary by CodeRabbit

  • New Features

    • Introduced full OAuth2 authorization code flow for Zapier integration with robust validation, concurrency control, and error handling.
    • Added support for dynamic dropdowns in Zapier actions and triggers (tenants, organizations, projects, tasks, contacts, teams).
    • Added new Zapier triggers for selecting tenants, organizations, projects, tasks, contacts, and teams.
    • Enhanced timer actions with new fields (description, isBillable, project, task, contact, team) and improved input selection.
    • Implemented webhook notifications for timer start/stop events.
    • Added new endpoints to fetch available Zapier triggers and actions.
  • Improvements

    • Strengthened security with allowed domains for OAuth redirects and validation of redirect URIs.
    • Improved error messages, logging, and validation throughout the OAuth and integration flows.
    • Enhanced sample data and help text for better user experience in Zapier actions.
  • Bug Fixes

    • Improved error handling and validation for OAuth flows and API interactions.
  • Chores

    • Upgraded Zapier dependencies to latest versions.
    • Updated environment and configuration files to support new OAuth features and security settings.
  • Documentation

    • Added help texts and dynamic labels to action fields for better clarity in the Zapier UI.

Walkthrough

This 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

File(s) / Path(s) Change Summary
.env.* Added GAUZY_ZAPIER_ALLOWED_DOMAINS, GAUZY_ZAPIER_MAX_AUTH_CODES, and GAUZY_ZAPIER_INSTANCE_COUNT environment variables to control allowed OAuth redirect domains, max auth codes, and instance count.
packages/common/src/lib/interfaces/IZapierConfig.ts Added allowedDomains, maxAuthCodes, and instanceCount properties to the Zapier config interface.
packages/config/src/lib/config/zapier.ts, .../environment.ts, .../environment.prod.ts Added/parsed allowedDomains, maxAuthCodes, and instanceCount in Zapier config objects; updated clientId/secret handling and postInstallUrl fallback.
packages/contracts/src/lib/zapier.model.ts Removed all Zapier model interfaces and types; replaced by local zapier.types.ts.
packages/contracts/src/index.ts Removed export of zapier.model to reflect deletion of that file.
packages/plugins/integration-zapier/src/lib/zapier.types.ts Added new Zapier-related types and interfaces including OAuth grant types, access tokens, integration input, webhook data, and endpoints.
packages/plugins/integration-zapier/src/lib/zapier-auth-code.service.ts New service for in-memory management of OAuth authorization codes with domain validation, expiration, periodic cleanup, and concurrency controls.
packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts Overhauled controller to implement full OAuth2 authorization code flow, including domain validation, concurrency controls for token refresh, detailed logging, and error handling. Added endpoints for authorization, callback, token exchange, and token refresh.
packages/plugins/integration-zapier/src/lib/zapier.controller.ts Refactored: removed old token/integration endpoints, added OAuth login success handler, endpoints to fetch Zapier triggers and actions, centralized token validation and error handling.
packages/plugins/integration-zapier/src/lib/zapier.service.ts Refactored: replaced HTTP calls with local UUID token generation, improved error handling and logging, streamlined integration creation and token refresh logic.
packages/plugins/integration-zapier/src/lib/zapier.module.ts Updated providers/imports: removed command/query handlers, added event handlers and new auth code service, updated exports and imports.
packages/plugins/integration-zapier/src/lib/handlers/* Added event handlers for timer started and stopped events, and an index file exporting them as EventHandlers.
packages/plugins/integration-zapier/src/lib/zapier-webhook.service.ts Added notifyTimerStatusChanged method to notify webhook subscribers of timer status changes; updated constructor to inject HTTP service.
packages/plugins/integration-zapier/src/lib/zapier-webhook-subscription.entity.ts Made integration property optional and nullable with detailed JSDoc comments.
packages/plugins/integration-zapier/src/lib/integration-zapier.plugin.ts Removed custom fields interface, added logger, injected config service, and updated logging in lifecycle hooks.
packages/plugins/integration-zapier/src/lib/zapier.config.ts Added constants for OAuth callback URL and token expiration time.
packages/plugins/integration-zapier/zapier/package.json Bumped zapier-platform-core and zapier-platform-cli dependencies to 16.5.0.
packages/plugins/integration-zapier/zapier/.gitignore Added *.tsbuildinfo to ignored files.
packages/plugins/integration-zapier/zapier/src/authentication.ts Enhanced OAuth2 config: explicit HTTP method and authorization headers for test request, added connectionLabel for user-friendly UI labels, improved test endpoint.
packages/plugins/integration-zapier/zapier/src/creates/startTimer.ts, stopTimer.ts Refined input fields with dynamic dropdowns and help text, improved error handling, updated request headers and body structure, enhanced sample data, and updated display descriptions using environment variables.
packages/plugins/integration-zapier/zapier/src/index.ts Added six new hidden triggers for dynamic dropdowns: tenant, organization, project, task, contact, and team lists.
packages/plugins/integration-zapier/zapier/src/triggers/*List.ts New files implementing dynamic dropdown triggers for tenants, organizations, projects, tasks, contacts, and teams with validation and error handling.
packages/plugins/integration-zapier/zapier/src/triggers/timerStatus.ts Added fallback polling function for timer status, improved error handling, updated sample data and response mapping.
packages/plugins/integration-zapier/src/lib/zapier-webhook.controller.ts Added deleteWebhook method; enhanced createWebhook with detailed Swagger docs, improved authorization header validation, and consistent error handling.
packages/core/src/lib/auth/auth.controller.ts, .../plugin-version.controller.ts Minor formatting and documentation corrections.
packages/config/src/lib/config/index.ts Added import and inclusion of zapier config module to the exported config array.
.scripts/icon-utils/icon-factory.ts Moved dotenv.config() call to the top for early environment variable loading.
packages/config/src/lib/environments/ienvironment.ts Swapped import order of IPosthogConfig and IZapierConfig interfaces.

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
Loading
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
Loading

Possibly related PRs

Suggested reviewers

  • evereq

Poem

In the warren of code, a new tunnel appears,
OAuth flows now hop without any fears.
Timer events leap, webhooks take flight,
Domains are checked for security tight.
Dropdowns bloom for each Zapier field,
With tokens and triggers, new powers revealed!
🐇✨ — The Rabbit Dev


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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@RolandM99 RolandM99 marked this pull request as draft April 7, 2025 20:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between c376006 and 9406216.

📒 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 local EventHandlers, 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 header

Adding the Content-Type header ensures the API server correctly processes the request body as JSON.


19-19: Proper boolean conversion

Converting 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 fields

The 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 default

Setting the default source to 'ZAPIER' instead of 'BROWSER' makes more sense for this integration.


94-141: Enhanced input fields with dynamic dropdowns

The 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 documentation

The 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 header

Adding the Content-Type header ensures the API server correctly processes the request body as JSON.


19-19: Proper boolean conversion

Converting 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 fields

The 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 dropdowns

The addition of descriptive fields with dynamic dropdowns for projects, tasks, contacts, and teams significantly improves the usability of the integration.

Copy link

nx-cloud bot commented Apr 7, 2025

View your CI Pipeline Execution ↗ for commit d7ca3da.

Command Status Duration Result
nx build gauzy -c=production --prod --verbose ✅ Succeeded 4m 18s View ↗
nx build api -c=production --prod ✅ Succeeded 1m 16s View ↗
nx build desktop-ui-lib --configuration=develop... ✅ Succeeded 34s View ↗
nx build desktop-ui-lib --configuration=production ✅ Succeeded 32s View ↗
nx build plugin-integration-wakatime ✅ Succeeded <1s View ↗
nx build desktop-lib ✅ Succeeded 7s View ↗
nx build desktop-window ✅ Succeeded <1s View ↗
nx build desktop-core ✅ Succeeded <1s View ↗
Additional runs (56) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2025-04-21 13:58:29 UTC

@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
@ever-co ever-co deleted a comment from coderabbitai bot Apr 9, 2025
Copy link

gitguardian bot commented Apr 9, 2025

️✅ 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.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 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.

@RolandM99 RolandM99 force-pushed the fix/zapier-plugin-integration branch from c2cebf6 to b43a04f Compare April 10, 2025 10:45
@RolandM99 RolandM99 marked this pull request as ready for review April 10, 2025 11:45
Copy link
Contributor

@greptile-apps greptile-apps bot left a 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 and ZapierTimerStoppedHandler 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 to ZapierWebhookService 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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 suggestion

Potential 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 using query.code instead of the freshly minted code. 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 default

The source field has inconsistent configuration:

  1. Default value is set to 'BROWSER' (line 90)
  2. '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 data

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9406216 and b43a04f.

📒 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 the IUserOrganization interface has an organizationId property.


🏁 Script executed:

#!/bin/bash
# Check the structure of the IUserOrganization interface
rg -A 15 "interface IUserOrganization" --type typescript

Length 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 accessing user.organizations[0].organizationId, please ensure that the IUserOrganization interface indeed includes the organizationId 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 the IUserOrganization 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 Interfaces

The fallback logic in currentOrganizationId() appears sound. However, since the IUserOrganization interface extends IBaseUserOrganization without explicitly specifying any properties, please confirm that the organizationId property is defined—either directly on IUserOrganization or within its base interface (IBaseUserOrganization). The grep results indicate that the property isn’t directly declared in IUserOrganization, so you'll need to verify the structure of IBaseUserOrganization.

  • Steps:
    • Manually inspect or run an additional search (e.g., using rg -A 20 "interface IBaseUserOrganization" .) to locate the definition of organizationId within IBaseUserOrganization.
    • 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:

  1. Using optional chaining for safer property access
  2. Ensuring consistent naming in the fallback connection label
  3. 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:

  1. Clear identification (key, noun)
  2. User-friendly display properties
  3. Properly configured input fields with dynamic dropdown
  4. 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 interface

The Tenant interface properly defines the structure expected from the API response with appropriate types.


33-48: LGTM! Well-structured export with clear metadata

The 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 dependencies

The 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 operations

Good addition of the performList function to the operations object, providing a polling fallback when webhooks aren't available.


113-146: LGTM! Comprehensive sample data structure

The 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 codes

The in-memory Map implementation with clear type definitions is appropriate for temporary auth codes.


51-69: LGTM! Secure implementation of code validation

Good 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 adding IZapierAccessTokens 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 for source 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 for description.
Makes the sample usage clearer.


153-153: Updated projectId reflects the new field.
Good consistency with the prior input definitions.


156-156: Added organizationTeamId 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 importing ZapierAuthCodeService nicely complements that approach.


45-53: Providers and exports alignment looks solid.

ZapierAuthCodeService and EventHandlers 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 and ZAPIER_TOKEN_EXPIRATION_TIME from zapier.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: New createIntegration 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 in authorize method.

  1. Validating zapier_redirect_uri, state, response_type, and client_id ensures a secure OAuth flow.
  2. Redirecting to the login page with relevant parameters is a sensible approach.

153-217: getToken method properly validates and creates integration tokens.

  1. Validating grant type, client credentials, and the authorization code is thorough.
  2. The use of this.zapierService.createIntegration standardizes token creation across the module.

219-278: refreshToken endpoint is aligned with service logic.

  1. Properly checks grant type and client credentials.
  2. 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 header

Adding the explicit 'Content-Type' header ensures proper handling of the JSON request body by the API.


19-19: Improved boolean conversion for isBillable

The strict equality check === 'true' is a good improvement for explicit boolean conversion.


41-41: Clear description for the Zapier action

The updated description clearly explains what this action does in Gauzy.


45-60: Good implementation of dynamic dropdown for tenant and organization

The dynamic dropdowns for tenant and organization IDs with clear help text improve user experience significantly.


93-99: Good addition of description field

Adding a description field with appropriate help text improves the usability of the timer functionality.


100-107: Improved isBillable field configuration

The billable field has been properly configured with the correct type (boolean) and helpful description.


109-140: Excellent implementation of dynamic dropdowns for related entities

The 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 values

The sample response has been properly updated with realistic UUID values and additional fields.

Also applies to: 150-156

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 than any, 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 suggestion

Include 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 issue

Fix 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 if TimerModule 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

📥 Commits

Reviewing files that changed from the base of the PR and between b43a04f and 71e2942.

📒 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 the Response 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 with encodeURIComponent
  • 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:

  1. Fetches user data from the API
  2. Creates a label based on user name or email with fallback
  3. Uses proper error handling with try/catch
  4. 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:

  1. Proper API request with authentication
  2. Response structure validation
  3. Error logging and meaningful error messages
  4. 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:

  1. Clear labels and descriptions
  2. Required field validation
  3. Dynamic reference to organization list
  4. 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:

  1. Clear labels and descriptions
  2. Required field validation
  3. Dynamic reference to organization list
  4. 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:

  1. Checking if the response exists
  2. 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.
The performList function cleanly handles potential errors and validates the response format before mapping the data. The error handling via the try/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.
Importing catchError, firstValueFrom, and of ensures modern RxJS practices without deprecated methods like toPromise().


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.
Using bundle.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 the catch 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.

  1. Dynamically populating tenant, organization, project, task, contact, and team IDs enhances usability.
  2. Providing help text for each field is excellent for guiding Zapier users.
  3. Using 'false' as the default string for the boolean isBillable 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 of EventHandlers.

Including EventHandlers suggests a shift to an event-driven pattern. Verify that each handler in EventHandlers 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 alongside ZapierService and ZapierWebhookService 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 for Map.

The generateAuthCode method internally mutates a shared Map. 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.

  1. Error handling: Appropriately raises NotFoundException and BadRequestException for missing settings.
  2. Performance: The for-loop to collect client_id, client_secret, and refresh_token is more performant than using a .reduce() with spreads.
  3. 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.

  1. Integration tenant: Using IntegrationTenantUpdateOrCreateCommand for consistent domain logic is a good approach.
  2. Dynamic entity settings: Mapping relevant organization and tenant IDs ensures each tied entity belongs to the correct scope.
  3. 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 the PermissionsEnum and the TenantPermissionGuard and others used below. No issues found.


24-24: Constructor injection looks fine
The zapierService 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 in validateToken.


35-43: Trigger fetching logic
The new /triggers endpoint validates the token and fetches triggers from zapierService.fetchTriggers(token). The error handling is appropriate.


45-63: Actions endpoint approach
The /actions endpoint mirrors /triggers, but uses validateToken(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
The validateToken() method throws BadRequestException or UnauthorizedException 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 sends query.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 sure process.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_URL

Length of output: 28


Action Required: Confirm CLIENT_BASE_URL Configuration
The authorize() method correctly validates the required parameters. However, the verification command did not output a defined CLIENT_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 a dynamic 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
Defining type: '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 default source remains 'BROWSER' in the sample, confirming the discrepancy with the summary.

Likely an incorrect or invalid review comment.

@RolandM99 RolandM99 removed the hold label Apr 16, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 flagged POSTHOG as an unknown word. Please add it to the cspell.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 that POSTHOG is not recognized. Add it to cspell.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 for POSTHOG, include it in your cspell.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, and INSTANCE_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 new ALLOWED_DOMAINS, MAX_AUTH_CODES, and INSTANCE_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 added ALLOWED_DOMAINS, MAX_AUTH_CODES, and INSTANCE_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

📥 Commits

Reviewing files that changed from the base of the PR and between f955235 and 2812744.

📒 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 global parseInt 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 global parseInt, 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(
Copy link
Collaborator

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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=1
packages/config/src/lib/environments/environment.prod.ts (1)

207-207: Simplify boolean coercion

The 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 failed

As 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2812744 and 5e062dc.

📒 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:

  1. Domain allowlist restricts OAuth redirects to trusted domains
  2. Max auth codes limit prevents potential memory issues
  3. 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:

  1. Restricting OAuth redirects to authorized domains only
  2. Limiting the number of stored authorization codes
  3. 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:

  1. Parses and trims domain values from environment variables
  2. Uses Number.parseInt for numeric conversion
  3. Provides sensible defaults (empty array, 1000 codes)
  4. 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:4200

This 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 configs

Here instanceCount is parsed to a number, but environment.prod.ts stores a boolean, and ZapierAuthCodeService 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 for baseUrl

this._config.get('baseUrl') assumes baseUrl is registered at the root level.
If the value lives under another namespace (e.g. environment.baseUrl) the call returns undefined, 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 casing

Some clients expect token_type to be lowercase (bearer). Confirm Zapier’s expectation and adjust if necessary:

- token_type: 'Bearer'
+ token_type: 'bearer'

Comment on lines +117 to +120
this.logger.debug('OAuth authorize request received');
this.logger.debug(`Redirect URI: ${redirectUri}`);
this.logger.debug(`State: ${state}`);

Copy link
Contributor

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.

Suggested change
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: ***`);

Comment on lines +190 to +197
// 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);

Copy link
Contributor

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.

Suggested change
// 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);

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 in environment.*.ts.
Because environment.prod.ts now sets allowedDomains as string[], calling .split on an array will throw TypeError: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5e062dc and e41352b.

📒 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 429

Cleaning up then throwing a 503 ServiceUnavailable may not convey the throttling nature of the limit.
A TooManyRequestsException(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 issue

Leaking 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 security

Likely an incorrect or invalid review comment.

Comment on lines +161 to 167
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();

Copy link
Contributor

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');
}

Comment on lines +216 to +222
settingsName: 'client_id',
settingsValue: client_id
},
{
settingsName: 'client_secret',
settingsValue: client_secret
},
Copy link
Contributor

@coderabbitai coderabbitai bot Apr 21, 2025

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.

Copy link
Collaborator

@rahul-rocket rahul-rocket Apr 21, 2025

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

Copy link
Contributor

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.

rahul-rocket and others added 5 commits April 21, 2025 17:54
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>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 suggestion

Harden 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 suggestion

DTO lacks runtime validation

@Body() body: IZapierCreateWebhookInput is injected without a ValidationPipe. Any missing/extra fields or wrong types will reach the service layer unchecked.
Register a global ValidationPipe 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 URLs

Passing plain strings like 'triggers' or 'actions' to HttpService.get() yields requests to http://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 whole firstValueFrom 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 exist

The 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.
Implement OnApplicationShutdown 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 between BadRequestException and UnauthorizedException based on a boolean flag. This is non‑obvious and was raised before.
Instead expose two explicit helpers (assertTokenProvided, assertTokenAuthorized) or always throw UnauthorizedException to simplify reasoning.

packages/plugins/integration-zapier/src/lib/zapier-authorization.controller.ts (8)

83-86: Avoid logging raw state values
state works as a CSRF token; exposing it verbatim can aid replay attacks if logs leak. Mask it the same way you already mask code.


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: Use ZapierGrantType 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 especially client_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 in getToken.


34-41: ⚠️ Potential issue

allowedDomains is never populated – all requests will fail domain validation

allowedDomains is initialised as an empty array and never assigned, so validateRedirectDomain() 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

📥 Commits

Reviewing files that changed from the base of the PR and between e41352b and 13d30dd.

📒 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.
Moving dotenv.config() immediately after importing dotenv 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:

  1. GAUZY_ZAPIER_ALLOWED_DOMAINS restricts OAuth redirects to a trusted list of domains, preventing open redirect vulnerabilities
  2. GAUZY_ZAPIER_MAX_AUTH_CODES prevents potential resource exhaustion attacks
  3. GAUZY_ZAPIER_INSTANCE_COUNT supports proper cleanup in multi-instance deployments

These 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:

  1. The domain parsing includes fallbacks, empty string filtering, and trimming
  2. Using Number.parseInt with a default value for maxAuthCodes ensures a valid number
  3. The instanceCount flag enables proper management in multi-instance deployments

These 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:

  1. Clear documentation of the ManyToOne relationship
  2. Making the integration relationship optional with ? syntax
  3. Adding proper JSDoc comments
  4. 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:

  1. Uses structured logging
  2. Retrieves credentials via ConfigService
  3. Validates OAuth configuration
  4. 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: Inconsistent GAUZY_ZAPIER_INSTANCE_COUNT interpretation across configs

Here the variable is parsed to boolean (=== 'true'),
whereas packages/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:

  1. Decide on one representation (boolean flag or numeric workers) and keep both files aligned.
  2. 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 types

Placing 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 issue

Sub‑domain validation is overly permissive

isSubdomain() matches any domain ending in allowedDomain, so evil.example.com.fake passes for example.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.

Comment on lines +44 to 54
controllers: [ZapierAuthorizationController, ZapierController, ZapierWebhookController],
providers: [
ZapierService,
ZapierWebhookService,
ZapierAuthCodeService,
MikroOrmZapierWebhookSubscriptionRepository,
TypeOrmZapierWebhookSubscriptionRepository,
...EventHandlers
],
exports: [ZapierService, ZapierWebhookService, ZapierAuthCodeService]
})
Copy link
Contributor

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:

  1. Uses event handlers instead of command/query handlers
  2. Includes the new ZapierAuthCodeService for OAuth code management
  3. 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.

Comment on lines +20 to 21
instanceCount: Number.parseInt(process.env.GAUZY_ZAPIER_INSTANCE_COUNT) || 1,

Copy link
Contributor

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.

Comment on lines +60 to +63
} catch (error) {
this.logger.error('Failed to create webhook subscription', error);
throw new InternalServerErrorException('Failed to create webhook subscription');
}
Copy link
Contributor

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.

Suggested change
} 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');
}

Comment on lines +93 to 95
const access_token = uuidv4();
const new_refresh_token = uuidv4();

Copy link
Contributor

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.

Comment on lines +127 to 135
@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');
}
}
Copy link
Contributor

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

@rahul-rocket rahul-rocket merged commit 0546c5c into develop Apr 21, 2025
12 of 17 checks passed
@rahul-rocket rahul-rocket deleted the fix/zapier-plugin-integration branch April 21, 2025 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants