Skip to content
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

feat:Add integration with Close CRM #484

Merged

Conversation

rajesh-jonnalagadda
Copy link
Contributor

@rajesh-jonnalagadda rajesh-jonnalagadda commented Jun 6, 2024

/claim #455

Added CRM integration

  • 1. user

  • 2. company

  • 3. contact

  • 4. engagement

  • 5. task

  • 6. note

  • 7. stage

demo video:
https://www.loom.com/share/ae1c51dc7d5546e69591007e5eddbe5a

Copy link

changeset-bot bot commented Jun 6, 2024

⚠️ No Changeset found

Latest commit: 7974e13

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Jun 6, 2024

@rajeshj11 is attempting to deploy a commit to the Panora Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Jun 6, 2024

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ naelob
❌ rajeshj11
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

coderabbitai bot commented Jun 6, 2024

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a new "Close" service integration across various CRM modules, including companies, contacts, deals, engagements, and more. This involves adding new service classes, mappers, and types to support the integration, as well as updating existing modules to incorporate the new service. Additionally, a new boolean column crm_close is added to the project_connectors table to track the usage of the Close service.

Changes

File(s) Change Summary
packages/api/scripts/init.sql Added a new boolean column crm_close to the project_connectors table.
.../crm/crm.connection.module.ts, .../crm/services/close/close.service.ts Introduced CloseConnectionService and updated data handling for tokens and OAuth refresh.
.../project-connectors/project-connectors.controller.ts, .../project-connectors.service.ts Added crm_close property to TypeCustom interface and ProjectConnectorsService class.
.../crm/services/close/types.ts, .../crm/services/close/mappers.ts, .../crm/services/close/index.ts Added new types, mappers, and service classes for Close integration in company, contact, deal, engagement, note, task, and stage modules.
.../crm/company/types/mappingsTypes.ts, .../crm/contact/types/mappingsTypes.ts, .../crm/deal/types/mappingsTypes.ts, .../crm/stage/types/mappingsTypes.ts Added Close mappers and mappings to respective unification mapping objects.
.../crm/company/company.module.ts, .../crm/contact/contact.module.ts, .../crm/deal/deal.module.ts, .../crm/engagement/engagement.module.ts Added CloseService to the list of services in the respective modules.

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant User
    participant API
    participant CloseService
    participant CRM
    participant Database

    User->>API: Request to add a new company
    API->>CloseService: Call addCompany method
    CloseService->>CRM: Send company data
    CRM-->>CloseService: Company data response
    CloseService->>Database: Save company data
    Database-->>CloseService: Confirmation
    CloseService-->>API: Company added response
    API-->>User: Company added confirmation
Loading
sequenceDiagram
    participant User
    participant API
    participant CloseService
    participant CRM
    participant Database

    User->>API: Request to sync contacts
    API->>CloseService: Call syncContacts method
    CloseService->>CRM: Fetch contacts
    CRM-->>CloseService: Contacts data
    CloseService->>Database: Update contacts data
    Database-->>CloseService: Confirmation
    CloseService-->>API: Contacts synced response
    API-->>User: Contacts synced confirmation
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

@rajesh-jonnalagadda
Copy link
Contributor Author

@rflihxyz Hey, can you please review the pr.

Copy link

@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

  • Added CloseConnectionService to CRM connection module
  • Introduced crm_close field in project_connectors table and service
  • Added Close CRM services and mappers for user, company, contact, engagement, task, and note
  • Updated unification mappings to support Close CRM
  • Enhanced OAuth token refresh process in CloseConnectionService

private registry: ServiceRegistry,
) {
this.logger.setContext(
CrmObject.company.toUpperCase() + ':' + CloseService.name,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a null check for connection to handle cases where no connection is found.

},
);
return {
data: resp?.data,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a return statement with an appropriate error response if an exception is caught.

CrmObject.company,
ActionType.POST,
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a null check for connection to handle cases where no connection is found.

statusCode: 200,
};
} catch (error) {
handleServiceError(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a return statement with an appropriate error response if an exception is caught.

addresses: source?.addresses?.map((address) => ({
address_1: address.street_1,
address_2: address.street_2,
city: address.city,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding validation for required fields in source to avoid potential runtime errors.

remote_id: string;
}[],
): Promise<CloseNoteInput> {
const result: CloseNoteInput = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors from getRemoteIdFromCompanyUuid to avoid unhandled promise rejections.

}

private async mapSingleNoteToUnified(
note: CloseNoteOutput,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure mapSingleNoteToUnified handles cases where customFieldMappings is undefined to avoid potential runtime errors.

if (note.contact_id) {
const contact_id = await this.utils.getContactUuidFromRemoteId(
note.contact_id,
'close',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors from getUserUuidFromRemoteId to avoid unhandled promise rejections.

opts = {
...opts,
contact_id: contact_id,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors from getContactUuidFromRemoteId to avoid unhandled promise rejections.

if (note.lead_id) {
const lead_id = await this.utils.getCompanyUuidFromRemoteId(
note.lead_id,
'close',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors from getCompanyUuidFromRemoteId to avoid unhandled promise rejections.

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

Outside diff range and nitpick comments (5)
packages/api/src/crm/task/services/close/index.ts (1)

14-104: Implementation of task services for Close CRM looks robust and follows best practices for security and error handling.

Consider adding more detailed documentation about error handling specifics and possible exceptions for better maintainability.

packages/api/src/crm/note/services/close/index.ts (1)

14-104: Implementation of note services for Close CRM is well-executed with appropriate security and error handling measures.

Enhance the logging details to include more context about the operations being performed for better traceability.

packages/api/src/crm/contact/services/close/index.ts (1)

14-106: Implementation of contact services for Close CRM looks robust and follows best practices for security and error handling.

Consider adding more detailed documentation about error handling specifics and possible exceptions for better maintainability.

packages/api/src/crm/engagement/sync/sync.service.ts (1)

Line range hint 29-29: Incorrect usage of the @Cron decorator.

The @Cron decorator is incorrectly placed inside a comment block. This will prevent it from being recognized by the NestJS framework. Please ensure that the decorator is placed directly above the method it is supposed to schedule.

-  //@Cron('*/2 * * * *') // every 2 minutes (for testing)
+  @Cron('*/2 * * * *') // every 2 minutes (for testing)
packages/shared/src/connectors/metadata.ts (1)

Line range hint 7-7: Consider simplifying the computed expressions as suggested by the static analysis tool.

The static analysis tool (Biome) suggests simplifying the computed expressions used in the keys of the JSON-like structure. This can improve readability and performance. Here's an example of how you might simplify one of the expressions:

- 'crm': { ... }
+ crm: { ... }

Please review all instances where this pattern occurs and apply the simplification if applicable.

Also applies to: 8-8, 20-20, 32-32, 44-44, 56-56, 68-68, 80-80, 92-92, 103-103, 114-114, 126-126, 136-136, 148-148, 159-159, 172-172, 184-184, 196-196, 207-207, 219-219

Tools
Biome

[error] 136-136: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 2f6db83 and f88d7e4.

Files selected for processing (46)
  • packages/api/scripts/init.sql (1 hunks)
  • packages/api/src/@core/connections/crm/crm.connection.module.ts (2 hunks)
  • packages/api/src/@core/connections/crm/services/close/close.service.ts (3 hunks)
  • packages/api/src/@core/project-connectors/project-connectors.controller.ts (1 hunks)
  • packages/api/src/@core/project-connectors/project-connectors.service.ts (1 hunks)
  • packages/api/src/@core/utils/types/original/original.crm.ts (10 hunks)
  • packages/api/src/crm/company/company.module.ts (2 hunks)
  • packages/api/src/crm/company/services/close/index.ts (1 hunks)
  • packages/api/src/crm/company/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/company/services/close/types.ts (1 hunks)
  • packages/api/src/crm/company/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/contact/contact.module.ts (2 hunks)
  • packages/api/src/crm/contact/services/close/index.ts (1 hunks)
  • packages/api/src/crm/contact/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/contact/services/close/types.ts (1 hunks)
  • packages/api/src/crm/contact/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/deal/deal.module.ts (2 hunks)
  • packages/api/src/crm/deal/services/close/index.ts (1 hunks)
  • packages/api/src/crm/deal/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/deal/services/close/types.ts (1 hunks)
  • packages/api/src/crm/deal/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/engagement/engagement.module.ts (2 hunks)
  • packages/api/src/crm/engagement/services/close/index.ts (1 hunks)
  • packages/api/src/crm/engagement/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/engagement/services/close/types.ts (1 hunks)
  • packages/api/src/crm/engagement/sync/sync.service.ts (1 hunks)
  • packages/api/src/crm/engagement/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/note/note.module.ts (2 hunks)
  • packages/api/src/crm/note/services/close/index.ts (1 hunks)
  • packages/api/src/crm/note/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/note/services/close/types.ts (1 hunks)
  • packages/api/src/crm/note/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/task/services/close/index.ts (1 hunks)
  • packages/api/src/crm/task/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/task/services/close/types.ts (1 hunks)
  • packages/api/src/crm/task/task.module.ts (2 hunks)
  • packages/api/src/crm/task/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/user/services/close/index.ts (1 hunks)
  • packages/api/src/crm/user/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/user/services/close/types.ts (1 hunks)
  • packages/api/src/crm/user/types/mappingsTypes.ts (2 hunks)
  • packages/api/src/crm/user/user.module.ts (2 hunks)
  • packages/shared/src/authUrl.ts (1 hunks)
  • packages/shared/src/connectors/enum.ts (1 hunks)
  • packages/shared/src/connectors/index.ts (1 hunks)
  • packages/shared/src/connectors/metadata.ts (1 hunks)
Files skipped from review due to trivial changes (2)
  • packages/api/src/crm/company/company.module.ts
  • packages/api/src/crm/user/services/close/types.ts
Additional context used
Biome
packages/api/src/@core/project-connectors/project-connectors.controller.ts

[error] 54-54: Decorators are not valid here. (parse)

Decorators are only valid on class declarations, class expressions, and class methods.
You can enable parameter decorators by setting the unsafeParameterDecoratorsEnabled option to true in your configuration file.


[error] 55-55: Decorators are not valid here. (parse)

Decorators are only valid on class declarations, class expressions, and class methods.
You can enable parameter decorators by setting the unsafeParameterDecoratorsEnabled option to true in your configuration file.


[error] 67-67: Decorators are not valid here. (parse)

Decorators are only valid on class declarations, class expressions, and class methods.
You can enable parameter decorators by setting the unsafeParameterDecoratorsEnabled option to true in your configuration file.


[error] 80-80: Decorators are not valid here. (parse)

Decorators are only valid on class declarations, class expressions, and class methods.
You can enable parameter decorators by setting the unsafeParameterDecoratorsEnabled option to true in your configuration file.

packages/api/src/crm/contact/services/close/mappers.ts

[error] 47-47: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

packages/shared/src/authUrl.ts

[error] 91-91: Useless rename. (lint/complexity/noUselessRename)

Safe fix: Remove the renaming.

packages/api/src/@core/connections/crm/services/close/close.service.ts

[error] 103-103: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 103-103: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 119-119: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 119-119: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

packages/api/src/crm/engagement/sync/sync.service.ts

[error] 29-29: Decorators are not valid here. (parse)

Decorators are only valid on class declarations, class expressions, and class methods.
You can enable parameter decorators by setting the unsafeParameterDecoratorsEnabled option to true in your configuration file.

packages/api/src/crm/engagement/services/close/mappers.ts

[error] 79-79: Change to an optional chain. (lint/complexity/useOptionalChain)

Unsafe fix: Change to an optional chain.

packages/shared/src/connectors/metadata.ts

[error] 7-7: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 8-8: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 20-20: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 32-32: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 44-44: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 56-56: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 68-68: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 80-80: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 92-92: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 103-103: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 114-114: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 126-126: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 136-136: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 148-148: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 159-159: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 172-172: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 184-184: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 196-196: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 207-207: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 219-219: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

Additional comments not posted (55)
packages/shared/src/connectors/index.ts (1)

1-1: Addition of 'close' to CRM_PROVIDERS aligns with the integration of Close CRM.

packages/shared/src/connectors/enum.ts (1)

7-7: Addition of 'CLOSE' to CrmConnectors enum supports the new CRM integration.

packages/api/src/crm/task/services/close/types.ts (1)

1-36: The new interfaces and types for Close CRM tasks are well-defined and align with the integration requirements.

packages/api/src/crm/note/services/close/types.ts (1)

1-42: The new interfaces and types for Close CRM notes are well-defined and align with the integration requirements.

packages/api/src/crm/deal/services/close/types.ts (1)

5-45: The definitions for CloseDealInput and CloseDealOutput are well-structured and align with TypeScript best practices.

packages/api/src/crm/deal/types/mappingsTypes.ts (1)

Line range hint 5-33: The integration of CloseDealMapper into the deal unification mappings is correctly implemented.

packages/api/src/crm/note/types/mappingsTypes.ts (1)

Line range hint 5-33: The addition of CloseNoteMapper to the note unification mappings is correctly implemented and follows the established architectural patterns.

packages/api/src/crm/task/types/mappingsTypes.ts (1)

Line range hint 5-33: The integration of CloseTaskMapper into the task unification mappings is correctly implemented and consistent with the system's architecture.

packages/api/src/crm/user/types/mappingsTypes.ts (2)

5-5: Import statement for CloseUserMapper added.

This aligns with the integration of the Close CRM service as described in the PR summary.


11-11: Integration of CloseUserMapper into userUnificationMapping.

The addition of Close CRM to the user mapping types is consistent with the overall goal of integrating Close CRM into the system. Ensure that the CloseUserMapper methods (unify and desunify) are implemented correctly and tested.

Also applies to: 30-33

packages/api/src/crm/contact/services/close/types.ts (1)

1-69: Comprehensive type definitions for Close CRM contacts.

The interfaces and types defined here are crucial for handling contact data within the Close CRM integration. Ensure that these types align with the actual data structures expected from the Close CRM API and that they are utilized correctly in the contact handling logic.

packages/api/src/crm/engagement/types/mappingsTypes.ts (2)

5-5: Import statement for CloseEngagementMapper added.

This is necessary for the integration of Close CRM services, specifically for engagement data mapping.


11-11: Integration of CloseEngagementMapper into engagementUnificationMapping.

The addition of Close CRM to the engagement mapping types is consistent with the integration goals. Verify that the CloseEngagementMapper methods (unify and desunify) are implemented correctly and tested.

Also applies to: 32-35

packages/api/src/crm/deal/deal.module.ts (1)

16-16: Integration of CloseService into the DealModule.

The addition of CloseService as a provider in the DealModule is essential for enabling Close CRM functionalities within the deal services. Ensure that CloseService is properly configured and that its dependencies are correctly injected.

Also applies to: 42-42

packages/api/src/crm/note/note.module.ts (2)

16-16: Import statement for CloseService is correctly placed and follows project standards.


42-42: Addition of CloseService to the providers array is correctly implemented.

packages/api/src/crm/task/task.module.ts (2)

16-16: Import statement for CloseService is correctly placed and follows project standards.


42-42: Addition of CloseService to the providers array is correctly implemented.

packages/api/src/crm/user/user.module.ts (2)

16-16: Import statement for CloseService is correctly placed and follows project standards.


42-42: Addition of CloseService to the providers array is correctly implemented.

packages/api/src/crm/user/services/close/mappers.ts (1)

19-33: Implementation of unify method in CloseUserMapper is robust and handles both single and array inputs effectively.

packages/api/src/crm/engagement/engagement.module.ts (2)

16-16: Import statement for CloseService is correctly placed and follows project structure.


42-42: CloseService correctly added to the providers array, ensuring it's available for dependency injection within the module.

packages/api/src/crm/contact/contact.module.ts (2)

17-17: Import statement for CloseService is correctly placed and follows project structure.


44-44: CloseService correctly added to the providers array, ensuring it's available for dependency injection within the module.

packages/api/src/@core/connections/crm/crm.connection.module.ts (2)

16-16: Import statement for CloseConnectionService is correctly placed and follows project structure.


35-35: CloseConnectionService correctly added to the providers array, ensuring it's available for dependency injection within the module.

packages/api/src/crm/contact/types/mappingsTypes.ts (2)

6-6: Import statement for CloseContactMapper is correctly placed and follows project structure.


36-39: CloseContactMapper correctly added to contactUnificationMapping, ensuring proper handling of contact data unification and de-unification.

packages/api/src/crm/company/types/mappingsTypes.ts (2)

6-6: Import statement for CloseCompanyMapper is correctly added.


36-39: Addition to companyUnificationMapping for Close CRM is correctly implemented.

packages/api/src/crm/user/services/close/index.ts (2)

13-25: Class definition and constructor setup for CloseService are correctly implemented.


27-66: syncUsers method in CloseService is well-implemented with proper error handling and security practices.

packages/api/src/@core/project-connectors/project-connectors.controller.ts (1)

33-33: Addition of crm_close property to TypeCustom interface is correctly implemented.

packages/api/src/crm/company/services/close/types.ts (1)

1-133: Type definitions in packages/api/src/crm/company/services/close/types.ts are comprehensive and correctly implemented.

packages/api/src/@core/project-connectors/project-connectors.service.ts (1)

66-66: The addition of crm_close property aligns with the integration requirements for Close CRM.

Verification successful

The crm_close property is correctly included in the TypeCustom interface and is passed to the service method createProjectConnectors. This confirms that the property is properly integrated into the data flow.

  • project-connectors.controller.ts: crm_close is part of the TypeCustom interface.
  • project-connectors.service.ts: crm_close is used in the createProjectConnectors method.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `crm_close` is properly utilized across the application.

# Test: Search for the usage of `crm_close` property. Expect: Proper handling in all relevant methods.
rg --type typescript 'crm_close'

Length of output: 67



Script:

#!/bin/bash
# Description: Verify that `crm_close` is properly utilized across the application.

# Test: Search for the usage of `crm_close` property. Expect: Proper handling in all relevant methods.
rg --glob '*.ts' 'crm_close'

Length of output: 232



Script:

#!/bin/bash
# Description: Verify that `crm_close` is properly utilized in the controller.

# Test: Search for the usage of `crm_close` property in the controller file.
rg 'crm_close' packages/api/src/@core/project-connectors/project-connectors.controller.ts

Length of output: 111



Script:

#!/bin/bash
# Description: Verify that `crm_close` is properly passed from the controller to the service.

# List all methods in the controller file.
ast-grep --lang typescript --pattern 'function $_($_) { $$$ }' packages/api/src/@core/project-connectors/project-connectors.controller.ts

Length of output: 139



Script:

#!/bin/bash
# Description: Verify that `crm_close` is properly passed from the controller to the service.

# List all method definitions in the controller file.
ast-grep --lang typescript --pattern 'class $_ { $$$ }' packages/api/src/@core/project-connectors/project-connectors.controller.ts

Length of output: 5250



Script:

#!/bin/bash
# Description: Verify that `crm_close` is included in the `TypeCustom` type.

# Search for the definition of `TypeCustom`.
rg 'TypeCustom' --glob '*.ts'

Length of output: 529



Script:

#!/bin/bash
# Description: Verify that `crm_close` is included in the `TypeCustom` interface.

# Extract the definition of the `TypeCustom` interface.
ast-grep --lang typescript --pattern 'interface TypeCustom { $$$ }' packages/api/src/@core/project-connectors/project-connectors.controller.ts

Length of output: 1543

packages/api/src/crm/deal/services/close/index.ts (2)

12-24: Constructor setup looks good and properly registers the service.


66-104: The syncDeals method is well-implemented with appropriate error handling and logging.

packages/api/src/crm/note/services/close/mappers.ts (2)

16-47: The desunify method correctly transforms unified note input to the Close CRM specific format, handling custom fields and company ID conversion effectively.


50-124: The unify method effectively handles both single and multiple Close CRM note outputs, converting them back to the unified format.

packages/api/src/crm/contact/services/close/mappers.ts (1)

63-110: The unify method effectively handles both single and multiple Close CRM contact outputs, converting them back to the unified format.

packages/api/src/crm/company/services/close/mappers.ts (2)

16-45: The desunify method correctly transforms unified company input to the Close CRM specific format, handling custom fields and address conversion effectively.


48-109: The unify method effectively handles both single and multiple Close CRM company outputs, converting them back to the unified format.

packages/api/src/crm/company/services/close/index.ts (1)

18-29: Ensure proper dependency injection and service registration.

The constructor correctly injects necessary services and registers the CloseService with the ServiceRegistry. This is a good practice for maintainability and modularity.

packages/api/src/crm/deal/services/close/mappers.ts (1)

16-59: Ensure asynchronous operations are handled correctly.

The desunify method correctly handles asynchronous operations and potential null values. Good use of async-await pattern ensures that the operations are non-blocking.

packages/shared/src/authUrl.ts (1)

109-109: Remove unnecessary renaming to simplify the code.

- const ignoreScopes = ['close']
+ const ignoreScopes = ['close'] // Remove unnecessary renaming

According to the static analysis tool Biome, the renaming here is unnecessary. Simplifying this can make the code cleaner and easier to understand.

Likely invalid or redundant comment.

packages/api/src/crm/engagement/services/close/types.ts (1)

13-13: Types and interfaces are well-defined and align with modular design principles.

Also applies to: 48-48, 50-52, 54-54, 90-90, 135-135, 194-194, 213-221

packages/api/src/@core/connections/crm/services/close/close.service.ts (2)

159-159: Ensure the grant_type is correctly set for refreshing tokens.


171-185: The token refresh logic correctly updates the database only on successful token refresh.

packages/api/src/@core/utils/types/original/original.crm.ts (2)

6-6: The import statements for Close CRM types are correctly added.

Also applies to: 24-26, 31-31, 49-51, 66-68, 99-101, 112-114


133-133: The union type definitions correctly include the Close CRM types.

Also applies to: 141-141, 150-150, 158-158, 166-166, 174-174, 191-191

packages/api/src/crm/engagement/services/close/index.ts (2)

22-34: The CloseService class is correctly annotated with @Injectable and implements the IEngagementService interface.


35-69: The methods for adding and syncing engagements are well-implemented with appropriate error handling.

Also applies to: 71-109, 111-149, 151-191, 194-219, 221-333

packages/api/src/crm/engagement/services/close/mappers.ts (1)

158-187: Ensure robust error handling in data mapping.

The unify method handles various engagement types and maps them to a unified format. Ensure that all possible engagement types are handled correctly and that error handling is robust, especially when dealing with external data sources.

Verification successful

Let's correct the file type and re-run the search to verify the handling of all engagement types in the unify method.


The previous script provided a comprehensive list of occurrences of the unify method across various files. However, it did not directly verify the handling of all engagement types within the unify method in packages/api/src/crm/engagement/services/close/mappers.ts.

To ensure robust error handling and proper engagement type management, we need to verify the specific implementation details of the unify method and its related methods (unifyCall, unifyMeeting, unifyEmail). Let's search for these methods within the same file to confirm their implementations.


The previous attempt to use ast-grep failed due to syntax errors in the pattern. Let's correct the patterns and re-run the search to verify the implementation details of the unify method and its related methods.


The previous attempts to use ast-grep failed due to syntax errors in the pattern. To ensure we gather the necessary information, let's use rg (ripgrep) to search for the method definitions directly within the specified file. This approach should help us verify the implementation details of the unify method and its related methods.


The rg search results confirm the presence of the unify method and its related methods (unifyCall, unifyMeeting, unifyEmail) in the specified file. However, to ensure robust error handling and proper engagement type management, we need to inspect the actual implementations of these methods.

Let's extract the full implementations of these methods to verify their correctness.


The extracted implementations of the unify method and its related methods (unifyCall, unifyMeeting, unifyEmail) confirm that the code handles different engagement types correctly. Each method checks if the source is an array and processes it accordingly, ensuring robust handling of both single and multiple engagement outputs.

The methods also map the engagement data to a unified format using specific mapping functions (mapSingleEngagementCallToUnified, mapSingleEngagementMeetingToUnified, mapSingleEngagementEmailToUnified).

Given this detailed inspection, the unify method and its related methods appear to be implemented correctly and handle various engagement types as expected.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that all engagement types are handled in the unify method.

# Test: Search for the method usage. Expect: Proper handling of all engagement types.
rg --type typescript $'unify'

Length of output: 63



Script:

#!/bin/bash
# Description: Verify that all engagement types are handled in the unify method.

# Test: Search for the method usage. Expect: Proper handling of all engagement types.
rg --type ts $'unify'

Length of output: 53547



Script:

#!/bin/bash
# Description: Verify the implementation details of the unify method and its related methods in the specified file.

# Search for the unify method and its related methods in the specified file.
ast-grep --lang typescript --pattern $'async unify($_, $_, $_) {
  $$$
}' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern $'private async unifyCall($_, $_) {
  $$$
}' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern $'private async unifyMeeting($_, $_) {
  $$$
}' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern $'private async unifyEmail($_, $_) {
  $$$
}' packages/api/src/crm/engagement/services/close/mappers.ts

Length of output: 2411



Script:

#!/bin/bash
# Description: Verify the implementation details of the unify method and its related methods in the specified file.

# Corrected patterns for searching the unify method and its related methods in the specified file.
ast-grep --lang typescript --pattern 'async unify($_, $_, $_) { $$$ }' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern 'private async unifyCall($_, $_) { $$$ }' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern 'private async unifyMeeting($_, $_) { $$$ }' packages/api/src/crm/engagement/services/close/mappers.ts

ast-grep --lang typescript --pattern 'private async unifyEmail($_, $_) { $$$ }' packages/api/src/crm/engagement/services/close/mappers.ts

Length of output: 2395



Script:

#!/bin/bash
# Description: Verify the implementation details of the unify method and its related methods in the specified file.

# Search for the unify method and its related methods in the specified file using ripgrep.
rg 'async unify' packages/api/src/crm/engagement/services/close/mappers.ts
rg 'private async unifyCall' packages/api/src/crm/engagement/services/close/mappers.ts
rg 'private async unifyMeeting' packages/api/src/crm/engagement/services/close/mappers.ts
rg 'private async unifyEmail' packages/api/src/crm/engagement/services/close/mappers.ts

Length of output: 532



Script:

#!/bin/bash
# Description: Extract the full implementations of the unify method and its related methods in the specified file.

# Extract the implementation of the unify method.
rg -A 20 'async unify' packages/api/src/crm/engagement/services/close/mappers.ts

# Extract the implementation of the unifyCall method.
rg -A 20 'private async unifyCall' packages/api/src/crm/engagement/services/close/mappers.ts

# Extract the implementation of the unifyMeeting method.
rg -A 20 'private async unifyMeeting' packages/api/src/crm/engagement/services/close/mappers.ts

# Extract the implementation of the unifyEmail method.
rg -A 20 'private async unifyEmail' packages/api/src/crm/engagement/services/close/mappers.ts

Length of output: 4422

packages/api/scripts/init.sql (1)

451-451: Addition of crm_close column to project_connectors table looks good.

Consider adding a comment to this column to explain its purpose, especially since it represents integration with a specific CRM system.

@@ -100,7 +100,7 @@ export class CloseConnectionService implements ICrmConnectionService {
data: {
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
account_url: CONNECTORS_METADATA['crm']['close'].urls.apiUrl,
account_url: CONNECTORS_METADATA['crm']['close']?.urls?.apiUrl,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify the computed expression for better readability.

- account_url: CONNECTORS_METADATA['crm']['close']?.urls?.apiUrl
+ account_url: CONNECTORS_METADATA.crm.close?.urls?.apiUrl
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.

Suggested change
account_url: CONNECTORS_METADATA['crm']['close']?.urls?.apiUrl,
account_url: CONNECTORS_METADATA.crm.close?.urls?.apiUrl,
Tools
Biome

[error] 103-103: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 103-103: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

Comment on lines +30 to +67
async addCompany(
companyData: CloseCompanyInput,
linkedUserId: string,
): Promise<ApiResponse<CloseCompanyOutput>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const resp = await axios.post(
`${connection.account_url}/lead/`,
JSON.stringify(companyData),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
},
);
return {
data: resp?.data,
message: 'Close company created',
statusCode: 201,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.company,
ActionType.POST,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential null values from external calls.

- const resp = await axios.post(
+ const resp = await axios.post(
+   `${connection?.account_url}/lead/`, // Ensure connection is not null before accessing account_url

Ensure that connection is not null before accessing account_url to avoid runtime errors.

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.

Suggested change
async addCompany(
companyData: CloseCompanyInput,
linkedUserId: string,
): Promise<ApiResponse<CloseCompanyOutput>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const resp = await axios.post(
`${connection.account_url}/lead/`,
JSON.stringify(companyData),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
},
);
return {
data: resp?.data,
message: 'Close company created',
statusCode: 201,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.company,
ActionType.POST,
);
}
async addCompany(
companyData: CloseCompanyInput,
linkedUserId: string,
): Promise<ApiResponse<CloseCompanyOutput>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const resp = await axios.post(
`${connection?.account_url}/lead/`,
JSON.stringify(companyData),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
},
);
return {
data: resp?.data,
message: 'Close company created',
statusCode: 201,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.company,
ActionType.POST,
);
}

Comment on lines +70 to +115
async syncCompanies(
linkedUserId: string,
custom_properties?: string[],
): Promise<ApiResponse<CloseCompanyOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});

const commonPropertyNames = Object.keys(commonCompanyCloseProperties);
const allProperties = [...commonPropertyNames, ...custom_properties];
const baseURL = `${connection.account_url}/lead/`;
const queryString = allProperties
.map((prop) => `properties=${encodeURIComponent(prop)}`)
.join('&');

const url = `${baseURL}?${queryString}`;

const resp = await axios.get(url, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
this.logger.log(`Synced close companies!`);

return {
data: resp?.data?.data,
message: 'Close companies retrieved',
statusCode: 200,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.company,
ActionType.GET,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize error handling and logging.

- handleServiceError(
+ handleServiceError(
+   error?.response?.data || error, // Provide more detailed error information if available

Enhance error handling by logging more detailed information if available. This can aid in debugging and provide clearer error messages to the end user.

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.

Suggested change
async syncCompanies(
linkedUserId: string,
custom_properties?: string[],
): Promise<ApiResponse<CloseCompanyOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const commonPropertyNames = Object.keys(commonCompanyCloseProperties);
const allProperties = [...commonPropertyNames, ...custom_properties];
const baseURL = `${connection.account_url}/lead/`;
const queryString = allProperties
.map((prop) => `properties=${encodeURIComponent(prop)}`)
.join('&');
const url = `${baseURL}?${queryString}`;
const resp = await axios.get(url, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
this.logger.log(`Synced close companies!`);
return {
data: resp?.data?.data,
message: 'Close companies retrieved',
statusCode: 200,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.company,
ActionType.GET,
);
}
async syncCompanies(
linkedUserId: string,
custom_properties?: string[],
): Promise<ApiResponse<CloseCompanyOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const commonPropertyNames = Object.keys(commonCompanyCloseProperties);
const allProperties = [...commonPropertyNames, ...custom_properties];
const baseURL = `${connection.account_url}/lead/`;
const queryString = allProperties
.map((prop) => `properties=${encodeURIComponent(prop)}`)
.join('&');
const url = `${baseURL}?${queryString}`;
const resp = await axios.get(url, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
this.logger.log(`Synced close companies!`);
return {
data: resp?.data?.data,
message: 'Close companies retrieved',
statusCode: 200,
};
} catch (error) {
handleServiceError(
error?.response?.data || error, // Provide more detailed error information if available
this.logger,
'Close',
CrmObject.company,
ActionType.GET,
);
}

Comment on lines +62 to +78
async unify(
source: CloseDealOutput | CloseDealOutput[],
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<UnifiedDealOutput | UnifiedDealOutput[]> {
if (!Array.isArray(source)) {
return await this.mapSingleDealToUnified(source, customFieldMappings);
}
// Handling array of CloseDealOutput
return Promise.all(
source.map((deal) =>
this.mapSingleDealToUnified(deal, customFieldMappings),
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize array handling in unify method.

- return Promise.all(
+ return Promise.all(
+   source.map(async (deal) => await this.mapSingleDealToUnified(deal, customFieldMappings)),

Use async-await inside the map function to ensure that each deal is processed asynchronously. This can improve the performance of the unify method when dealing with large arrays.

Committable suggestion was skipped due to low confidence.

Comment on lines +15 to +57
async desunify(
source: UnifiedTaskInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<CloseTaskInput> {
const result: CloseTaskInput = {
text: source?.content ?? '',
is_complete: source.status === 'COMPLETED',
_type: 'lead',
lead_id: '',
assigned_to: '',
date: '',
};

if (source.user_id) {
const owner_id = await this.utils.getRemoteIdFromUserUuid(source.user_id);
if (owner_id) {
result.assigned_to = owner_id;
}
}
if (source.company_id) {
const company_id = await this.utils.getRemoteIdFromCompanyUuid(
source.company_id,
);
if (company_id) {
result.lead_id = company_id;
}
}

if (customFieldMappings && source.field_mappings) {
for (const [k, v] of Object.entries(source.field_mappings)) {
const mapping = customFieldMappings.find(
(mapping) => mapping.slug === k,
);
if (mapping) {
result[mapping.remote_id] = v;
}
}
}

return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate external data before processing.

- const result: CloseTaskInput = {
+ const result: CloseTaskInput = {
+   ...source, // Ensure all necessary fields are initialized from source to avoid missing data

Ensure that all necessary fields are initialized from the source object to avoid processing incomplete data. This can prevent runtime errors and ensure data integrity.

Committable suggestion was skipped due to low confidence.

Comment on lines +252 to +458
opts = {
...opts,
contact_id: contact_id,
};
}
}
if (engagement.lead_id) {
const lead_id = await this.utils.getCompanyUuidFromRemoteId(
engagement.lead_id,
'close',
);
if (lead_id) {
opts = {
...opts,
company_id: lead_id,
};
}
}

return {
remote_id: engagement.id,
content: engagement.note,
subject: engagement.title,
start_at: new Date(engagement.starts_at),
end_time: new Date(engagement.ends_at),
type: 'MEETING',
field_mappings,
duration: engagement.duration,
...opts,
};
}

private async mapSingleEngagementEmailToUnified(
engagement: CloseEngagementEmailOutput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<UnifiedEngagementOutput> {
const field_mappings: { [key: string]: any } = {};
if (customFieldMappings) {
for (const mapping of customFieldMappings) {
field_mappings[mapping.slug] = engagement[mapping.remote_id];
}
}

let opts: any = {};
if (engagement.user_id) {
const owner_id = await this.utils.getUserUuidFromRemoteId(
engagement.user_id,
'close',
);
if (owner_id) {
opts = {
user_id: owner_id,
};
}
}
if (engagement.contact_id) {
const contact_id = await this.utils.getContactUuidFromRemoteId(
engagement.contact_id,
'close',
);
if (contact_id) {
opts = {
...opts,
contact_id: contact_id,
};
}
}
if (engagement.lead_id) {
const lead_id = await this.utils.getCompanyUuidFromRemoteId(
engagement.lead_id,
'close',
);
if (lead_id) {
opts = {
...opts,
company_id: lead_id,
};
}
}

return {
remote_id: engagement.id,
content: engagement.body_html,
subject: '',
start_at: new Date(engagement.date_created),
end_time: new Date(engagement.date_updated), // Assuming end time can be mapped from last modified date
type: 'EMAIL',
direction:
engagement.direction === 'outgoing'
? 'OUTBOUND'
: engagement.direction === 'inbound'
? 'INBOUND'
: '',
field_mappings,
...opts,
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize data mapping functions.

The methods mapSingleEngagementCallToUnified, mapSingleEngagementMeetingToUnified, and mapSingleEngagementEmailToUnified perform similar operations. Consider refactoring these methods to reduce code duplication and improve maintainability.

source.company_id,
);
}
if (source?.contacts && source?.contacts?.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use optional chaining for safer access.

The access to nested properties in the source.contacts array should use optional chaining to prevent runtime errors if contacts is undefined or empty.

- if (source?.contacts && source?.contacts?.length) {
+ if (source?.contacts?.length) {
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.

Suggested change
if (source?.contacts && source?.contacts?.length) {
if (source?.contacts?.length) {
Tools
Biome

[error] 79-79: Change to an optional chain. (lint/complexity/useOptionalChain)

Unsafe fix: Change to an optional chain.

Comment on lines +9 to +17
desunify(
source: UnifiedUserInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): CloseUserInput {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete implementation of desunify method in CloseUserMapper.

Please complete the method implementation to correctly map from UnifiedUserInput to CloseUserInput.

Comment on lines +25 to +63
async addDeal(
dealData: CloseDealInput,
linkedUserId: string,
): Promise<ApiResponse<CloseDealOutput>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'close',
vertical: 'crm',
},
});
const resp = await axios.post(
`${connection.account_url}/opportunity/`,
JSON.stringify(dealData),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
},
);

return {
data: resp?.data,
message: 'Close deal created',
statusCode: 201,
};
} catch (error) {
handleServiceError(
error,
this.logger,
'Close',
CrmObject.deal,
ActionType.POST,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addDeal method implementation is robust with proper error handling. Consider adding specific error logging in the catch block for better traceability.

+ this.logger.error(`Error adding deal: ${error.message}`);

Committable suggestion was skipped due to low confidence.

Comment on lines 21 to 60
async desunify(
source: UnifiedContactInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<CloseContactInput> {
// Assuming 'email_addresses' array contains at least one email and 'phone_numbers' array contains at least one phone number
const result: CloseContactInput = {
name: `${source.first_name ?? ''} ${source.last_name ?? ''}`,
phones: source?.phone_numbers?.map(
({ phone_number, phone_type }) =>
({
phone: phone_number,
type: phone_type,
} as InputPhone),
),
emails: source?.email_addresses?.map(
({ email_address, email_address_type }) =>
({
email: email_address,
type: email_address_type,
} as InputEmail),
),
};

result.lead_id = source?.field_mappings?.['company_id'];

if (customFieldMappings && source.field_mappings) {
for (const [k, v] of Object.entries(source.field_mappings)) {
const mapping = customFieldMappings.find(
(mapping) => mapping.slug === k,
);
if (mapping) {
result[mapping.remote_id] = v;
}
}
}

return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desunify method effectively transforms unified contact input to the Close CRM specific format, handling custom fields and lead ID conversion well. Consider simplifying the expression for accessing company_id.

- result.lead_id = source?.field_mappings?.['company_id'];
+ result.lead_id = source.field_mappings.company_id;
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.

Suggested change
async desunify(
source: UnifiedContactInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<CloseContactInput> {
// Assuming 'email_addresses' array contains at least one email and 'phone_numbers' array contains at least one phone number
const result: CloseContactInput = {
name: `${source.first_name ?? ''} ${source.last_name ?? ''}`,
phones: source?.phone_numbers?.map(
({ phone_number, phone_type }) =>
({
phone: phone_number,
type: phone_type,
} as InputPhone),
),
emails: source?.email_addresses?.map(
({ email_address, email_address_type }) =>
({
email: email_address,
type: email_address_type,
} as InputEmail),
),
};
result.lead_id = source?.field_mappings?.['company_id'];
if (customFieldMappings && source.field_mappings) {
for (const [k, v] of Object.entries(source.field_mappings)) {
const mapping = customFieldMappings.find(
(mapping) => mapping.slug === k,
);
if (mapping) {
result[mapping.remote_id] = v;
}
}
}
return result;
async desunify(
source: UnifiedContactInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<CloseContactInput> {
// Assuming 'email_addresses' array contains at least one email and 'phone_numbers' array contains at least one phone number
const result: CloseContactInput = {
name: `${source.first_name ?? ''} ${source.last_name ?? ''}`,
phones: source?.phone_numbers?.map(
({ phone_number, phone_type }) =>
({
phone: phone_number,
type: phone_type,
} as InputPhone),
),
emails: source?.email_addresses?.map(
({ email_address, email_address_type }) =>
({
email: email_address,
type: email_address_type,
} as InputEmail),
),
};
result.lead_id = source.field_mappings.company_id;
if (customFieldMappings && source.field_mappings) {
for (const [k, v] of Object.entries(source.field_mappings)) {
const mapping = customFieldMappings.find(
(mapping) => mapping.slug === k,
);
if (mapping) {
result[mapping.remote_id] = v;
}
}
}
return result;
Tools
Biome

[error] 47-47: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

Copy link

@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)

  • Added CloseService for Close CRM stage integration
  • Introduced CloseStageMapper for stage data conversion
  • Added CloseStageInput and CloseStageOutput types
  • Updated stage.module.ts to include CloseService
  • Integrated CloseStageMapper into stageUnificationMapping

);
this.registry.registerService('close', this);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if connection is null before proceeding to avoid potential null reference errors.

async syncStages(
linkedUserId: string,
deal_id: string,
custom_properties?: string[],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if res is null before accessing res.remote_id to prevent runtime errors.

): Promise<ApiResponse<CloseStageOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding error handling for the Axios request to manage potential HTTP errors.

const baseURL = `${connection.account_url}/activity/status_change/opportunity/?opportunity_id=${res.remote_id}`;

const resp = await axios.get(baseURL, {
headers: {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return a meaningful error response or status code in the catch block to inform the caller of the failure.

Comment on lines +10 to +12
source: UnifiedStageInput,
customFieldMappings?: {
slug: string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desunify method currently returns an empty object. Ensure this is implemented before merging.

Comment on lines +24 to +26
}[],
): UnifiedStageOutput | UnifiedStageOutput[] {
if (!Array.isArray(source)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding error handling for cases where source is not an array and not a valid CloseStageOutput object.

change the key expected value to vlaue
Copy link

@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)

  • Modified amount field to use deal.value instead of deal.expected_value
  • Removed an extra line for better readability in mappers.ts

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f88d7e4 and 799529b.

Files selected for processing (8)
  • packages/api/src/@core/connections/crm/services/close/close.service.ts (4 hunks)
  • packages/api/src/@core/utils/types/original/original.crm.ts (11 hunks)
  • packages/api/src/crm/deal/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/stage/services/close/index.ts (1 hunks)
  • packages/api/src/crm/stage/services/close/mappers.ts (1 hunks)
  • packages/api/src/crm/stage/services/close/types.ts (1 hunks)
  • packages/api/src/crm/stage/stage.module.ts (2 hunks)
  • packages/api/src/crm/stage/types/mappingsTypes.ts (2 hunks)
Files skipped from review as they are similar to previous changes (2)
  • packages/api/src/@core/utils/types/original/original.crm.ts
  • packages/api/src/crm/deal/services/close/mappers.ts
Additional context used
Biome
packages/api/src/@core/connections/crm/services/close/close.service.ts

[error] 100-100: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 100-100: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 116-116: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 116-116: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

Additional comments not posted (14)
packages/api/src/crm/stage/types/mappingsTypes.ts (2)

5-5: Import statement for CloseStageMapper added.

This ensures that the new Close CRM integration can utilize the mapping functionalities.


11-11: Integration of CloseStageMapper into the stageUnificationMapping.

This addition correctly aligns with the structure used for other CRM services, maintaining consistency in the codebase.

Also applies to: 30-33

packages/api/src/crm/stage/services/close/types.ts (3)

1-17: Interface CloseStageInput defined.

This interface is well-structured and includes optional fields, which provides flexibility in data handling.


19-46: Interface OpportunityStatusChange defined.

This interface is comprehensive and covers all necessary fields for tracking changes in opportunity status, which is crucial for CRM functionalities.


48-52: Type alias CloseStageOutput and constant commonStageCloseProperties defined.

The partial type for CloseStageOutput is a good use of TypeScript's utility types to allow flexibility, and the constant provides a centralized place for common properties.

packages/api/src/crm/stage/stage.module.ts (2)

16-16: Import statement for CloseService added.

This import is necessary for the integration of the Close CRM service into the module.


42-42: CloseService added to module providers.

Properly registering CloseService as a provider ensures it can be injected where needed in the application.

packages/api/src/crm/stage/services/close/mappers.ts (2)

8-17: Method desunify implementation is missing.

The previous comment by greptile-apps[bot] is still valid. Ensure this method is implemented before merging.


19-33: Method unify handles both single and array inputs of CloseStageOutput.

The method correctly differentiates between single and array inputs, ensuring proper data handling. The previous comment about error handling has been addressed by checking if the source is an array.

packages/api/src/crm/stage/services/close/index.ts (4)

26-26: Check if connection is null before proceeding to avoid potential null reference errors.


30-30: Check if res is null before accessing res.remote_id to prevent runtime errors.


34-34: Consider adding error handling for the Axios request to manage potential HTTP errors.


47-47: Return a meaningful error response or status code in the catch block to inform the caller of the failure.

packages/api/src/@core/connections/crm/services/close/close.service.ts (1)

100-100: Simplify the computed expression for better readability.

- account_url: CONNECTORS_METADATA['crm']['close']?.urls?.apiUrl
+ account_url: CONNECTORS_METADATA.crm.close?.urls?.apiUrl
Tools
Biome

[error] 100-100: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.


[error] 100-100: The computed expression can be simplified without the use of a string literal. (lint/complexity/useLiteralKeys)

Unsafe fix: Use a literal key instead.

@naelob
Copy link
Contributor

naelob commented Jun 6, 2024

@rflihxyz Hey, can you please review the pr.

going to check that ! awesome work !

@rajesh-jonnalagadda
Copy link
Contributor Author

@naelob Hey, when can I expect the feedback for these changes?

@rajesh-jonnalagadda rajesh-jonnalagadda marked this pull request as draft June 11, 2024 19:27
@rajesh-jonnalagadda rajesh-jonnalagadda marked this pull request as ready for review June 12, 2024 07:35
@naelob naelob merged commit 8dd2c06 into panoratech:main Jun 12, 2024
8 of 12 checks passed
This was referenced Sep 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants