-
Notifications
You must be signed in to change notification settings - Fork 194
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
feat:Add integration with Close CRM #484
Conversation
|
@rajeshj11 is attempting to deploy a commit to the Panora Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThe 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 Changes
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
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
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? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
@rflihxyz Hey, can you please review the pr. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
- Added
CloseConnectionService
to CRM connection module - Introduced
crm_close
field inproject_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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a null check for connection
to handle cases where no connection is found.
}, | ||
); | ||
return { | ||
data: resp?.data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a return statement with an appropriate error response if an exception is caught.
CrmObject.company, | ||
ActionType.POST, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a null check for connection
to handle cases where no connection is found.
statusCode: 200, | ||
}; | ||
} catch (error) { | ||
handleServiceError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding validation for required fields in source
to avoid potential runtime errors.
remote_id: string; | ||
}[], | ||
): Promise<CloseNoteInput> { | ||
const result: CloseNoteInput = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle potential errors from getRemoteIdFromCompanyUuid
to avoid unhandled promise rejections.
} | ||
|
||
private async mapSingleNoteToUnified( | ||
note: CloseNoteOutput, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle potential errors from getUserUuidFromRemoteId
to avoid unhandled promise rejections.
opts = { | ||
...opts, | ||
contact_id: contact_id, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle potential errors from getCompanyUuidFromRemoteId
to avoid unhandled promise rejections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
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 theunsafeParameterDecoratorsEnabled
option totrue
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 theunsafeParameterDecoratorsEnabled
option totrue
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 theunsafeParameterDecoratorsEnabled
option totrue
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 theunsafeParameterDecoratorsEnabled
option totrue
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 theunsafeParameterDecoratorsEnabled
option totrue
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 forCloseDealInput
andCloseDealOutput
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 ofCloseDealMapper
into the deal unification mappings is correctly implemented.packages/api/src/crm/note/types/mappingsTypes.ts (1)
Line range hint
5-33
: The addition ofCloseNoteMapper
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 ofCloseTaskMapper
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 forCloseUserMapper
added.This aligns with the integration of the Close CRM service as described in the PR summary.
11-11
: Integration ofCloseUserMapper
intouserUnificationMapping
.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
anddesunify
) 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 forCloseEngagementMapper
added.This is necessary for the integration of Close CRM services, specifically for engagement data mapping.
11-11
: Integration ofCloseEngagementMapper
intoengagementUnificationMapping
.The addition of Close CRM to the engagement mapping types is consistent with the integration goals. Verify that the
CloseEngagementMapper
methods (unify
anddesunify
) are implemented correctly and tested.Also applies to: 32-35
packages/api/src/crm/deal/deal.module.ts (1)
16-16
: Integration ofCloseService
into theDealModule
.The addition of
CloseService
as a provider in theDealModule
is essential for enabling Close CRM functionalities within the deal services. Ensure thatCloseService
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 forCloseService
is correctly placed and follows project standards.
42-42
: Addition ofCloseService
to the providers array is correctly implemented.packages/api/src/crm/task/task.module.ts (2)
16-16
: Import statement forCloseService
is correctly placed and follows project standards.
42-42
: Addition ofCloseService
to the providers array is correctly implemented.packages/api/src/crm/user/user.module.ts (2)
16-16
: Import statement forCloseService
is correctly placed and follows project standards.
42-42
: Addition ofCloseService
to the providers array is correctly implemented.packages/api/src/crm/user/services/close/mappers.ts (1)
19-33
: Implementation ofunify
method inCloseUserMapper
is robust and handles both single and array inputs effectively.packages/api/src/crm/engagement/engagement.module.ts (2)
16-16
: Import statement forCloseService
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 forCloseService
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 forCloseConnectionService
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 forCloseContactMapper
is correctly placed and follows project structure.
36-39
:CloseContactMapper
correctly added tocontactUnificationMapping
, ensuring proper handling of contact data unification and de-unification.packages/api/src/crm/company/types/mappingsTypes.ts (2)
6-6
: Import statement forCloseCompanyMapper
is correctly added.
36-39
: Addition tocompanyUnificationMapping
for Close CRM is correctly implemented.packages/api/src/crm/user/services/close/index.ts (2)
13-25
: Class definition and constructor setup forCloseService
are correctly implemented.
27-66
:syncUsers
method inCloseService
is well-implemented with proper error handling and security practices.packages/api/src/@core/project-connectors/project-connectors.controller.ts (1)
33-33
: Addition ofcrm_close
property toTypeCustom
interface is correctly implemented.packages/api/src/crm/company/services/close/types.ts (1)
1-133
: Type definitions inpackages/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 ofcrm_close
property aligns with the integration requirements for Close CRM.Verification successful
The
crm_close
property is correctly included in theTypeCustom
interface and is passed to the service methodcreateProjectConnectors
. This confirms that the property is properly integrated into the data flow.
project-connectors.controller.ts
:crm_close
is part of theTypeCustom
interface.project-connectors.service.ts
:crm_close
is used in thecreateProjectConnectors
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.tsLength 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.tsLength 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.tsLength 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.tsLength 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
: ThesyncDeals
method is well-implemented with appropriate error handling and logging.packages/api/src/crm/note/services/close/mappers.ts (2)
16-47
: Thedesunify
method correctly transforms unified note input to the Close CRM specific format, handling custom fields and company ID conversion effectively.
50-124
: Theunify
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
: Theunify
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
: Thedesunify
method correctly transforms unified company input to the Close CRM specific format, handling custom fields and address conversion effectively.
48-109
: Theunify
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 theServiceRegistry
. 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 renamingAccording 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 thegrant_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
: TheCloseService
class is correctly annotated with@Injectable
and implements theIEngagementService
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 theunify
method inpackages/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 theunify
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 userg
(ripgrep) to search for the method definitions directly within the specified file. This approach should help us verify the implementation details of theunify
method and its related methods.
The
rg
search results confirm the presence of theunify
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.tsLength 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.tsLength 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.tsLength 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.tsLength of output: 4422
packages/api/scripts/init.sql (1)
451-451
: Addition ofcrm_close
column toproject_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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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, | |
); | |
} |
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, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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, | |
); | |
} |
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), | ||
), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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, | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
desunify( | ||
source: UnifiedUserInput, | ||
customFieldMappings?: { | ||
slug: string; | ||
remote_id: string; | ||
}[], | ||
): CloseUserInput { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete implementation of desunify
method in CloseUserMapper
.
Please complete the method implementation to correctly map from UnifiedUserInput
to CloseUserInput
.
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, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
(updates since last review)
- Added
CloseService
for Close CRM stage integration - Introduced
CloseStageMapper
for stage data conversion - Added
CloseStageInput
andCloseStageOutput
types - Updated
stage.module.ts
to includeCloseService
- Integrated
CloseStageMapper
intostageUnificationMapping
); | ||
this.registry.registerService('close', this); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check if connection
is null before proceeding to avoid potential null reference errors.
async syncStages( | ||
linkedUserId: string, | ||
deal_id: string, | ||
custom_properties?: string[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return a meaningful error response or status code in the catch block to inform the caller of the failure.
source: UnifiedStageInput, | ||
customFieldMappings?: { | ||
slug: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The desunify
method currently returns an empty object. Ensure this is implemented before merging.
}[], | ||
): UnifiedStageOutput | UnifiedStageOutput[] { | ||
if (!Array.isArray(source)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
(updates since last review)
- Modified
amount
field to usedeal.value
instead ofdeal.expected_value
- Removed an extra line for better readability in
mappers.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
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 forCloseStageMapper
added.This ensures that the new Close CRM integration can utilize the mapping functionalities.
11-11
: Integration ofCloseStageMapper
into thestageUnificationMapping
.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
: InterfaceCloseStageInput
defined.This interface is well-structured and includes optional fields, which provides flexibility in data handling.
19-46
: InterfaceOpportunityStatusChange
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 aliasCloseStageOutput
and constantcommonStageCloseProperties
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 forCloseService
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
: Methoddesunify
implementation is missing.The previous comment by greptile-apps[bot] is still valid. Ensure this method is implemented before merging.
19-33
: Methodunify
handles both single and array inputs ofCloseStageOutput
.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 ifconnection
is null before proceeding to avoid potential null reference errors.
30-30
: Check ifres
is null before accessingres.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?.apiUrlTools
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.
going to check that ! awesome work ! |
@naelob Hey, when can I expect the feedback for these changes? |
/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