From 9c763028e0b2e2b1bc639925b1e7493251278826 Mon Sep 17 00:00:00 2001 From: nael Date: Tue, 11 Jun 2024 23:16:04 +0200 Subject: [PATCH 1/9] feat: fix jira oauth --- .env.example | 5 + docker-compose.dev.yml | 24 +- packages/api/prisma/schema.prisma | 63 +++ packages/api/scripts/init.sql | 1 + packages/api/scripts/seed.sql | 8 +- .../connections/connections.controller.ts | 4 +- .../connections/crm/crm.connection.module.ts | 2 + .../crm/services/close/close.service.ts | 36 +- .../ticketing/services/jira/jira.service.ts | 27 +- .../@core/encryption/encryption.service.ts | 2 +- .../project-connectors.controller.ts | 1 + .../project-connectors.service.ts | 1 + packages/api/src/@core/utils/errors.ts | 6 +- .../utils/types/original/original.crm.ts | 74 ++- packages/api/src/crm/@lib/@types/index.ts | 2 +- .../api/src/crm/company/company.module.ts | 2 + .../src/crm/company/services/close/index.ts | 117 +++++ .../src/crm/company/services/close/mappers.ts | 111 +++++ .../src/crm/company/services/close/types.ts | 133 +++++ .../src/crm/company/types/mappingsTypes.ts | 6 + .../api/src/crm/contact/contact.module.ts | 2 + .../src/crm/contact/services/close/index.ts | 107 ++++ .../src/crm/contact/services/close/mappers.ts | 111 +++++ .../src/crm/contact/services/close/types.ts | 69 +++ .../src/crm/contact/types/mappingsTypes.ts | 6 + packages/api/src/crm/deal/deal.module.ts | 2 + .../api/src/crm/deal/services/close/index.ts | 106 ++++ .../src/crm/deal/services/close/mappers.ts | 141 ++++++ .../api/src/crm/deal/services/close/types.ts | 45 ++ .../api/src/crm/deal/types/mappingsTypes.ts | 6 + .../src/crm/engagement/engagement.module.ts | 2 + .../crm/engagement/services/close/index.ts | 334 +++++++++++++ .../crm/engagement/services/close/mappers.ts | 459 ++++++++++++++++++ .../crm/engagement/services/close/types.ts | 221 +++++++++ .../src/crm/engagement/sync/sync.service.ts | 4 +- .../src/crm/engagement/types/mappingsTypes.ts | 6 + packages/api/src/crm/note/note.module.ts | 2 + .../api/src/crm/note/services/close/index.ts | 105 ++++ .../src/crm/note/services/close/mappers.ts | 125 +++++ .../api/src/crm/note/services/close/types.ts | 42 ++ .../api/src/crm/note/types/mappingsTypes.ts | 6 + .../api/src/crm/stage/services/close/index.ts | 70 +++ .../src/crm/stage/services/close/mappers.ts | 54 +++ .../api/src/crm/stage/services/close/types.ts | 52 ++ packages/api/src/crm/stage/stage.module.ts | 2 + .../api/src/crm/stage/types/mappingsTypes.ts | 6 + .../api/src/crm/task/services/close/index.ts | 105 ++++ .../src/crm/task/services/close/mappers.ts | 140 ++++++ .../api/src/crm/task/services/close/types.ts | 36 ++ packages/api/src/crm/task/task.module.ts | 2 + .../api/src/crm/task/types/mappingsTypes.ts | 6 + .../api/src/crm/user/services/close/index.ts | 67 +++ .../src/crm/user/services/close/mappers.ts | 55 +++ .../api/src/crm/user/services/close/types.ts | 14 + .../api/src/crm/user/types/mappingsTypes.ts | 6 + packages/api/src/crm/user/user.module.ts | 2 + packages/api/swagger/swagger-spec.json | 2 +- packages/shared/src/authUrl.ts | 8 +- packages/shared/src/connectors/enum.ts | 3 +- packages/shared/src/connectors/index.ts | 2 +- packages/shared/src/connectors/metadata.ts | 5 +- 61 files changed, 3084 insertions(+), 79 deletions(-) create mode 100644 packages/api/src/crm/company/services/close/index.ts create mode 100644 packages/api/src/crm/company/services/close/mappers.ts create mode 100644 packages/api/src/crm/company/services/close/types.ts create mode 100644 packages/api/src/crm/contact/services/close/index.ts create mode 100644 packages/api/src/crm/contact/services/close/mappers.ts create mode 100644 packages/api/src/crm/contact/services/close/types.ts create mode 100644 packages/api/src/crm/deal/services/close/index.ts create mode 100644 packages/api/src/crm/deal/services/close/mappers.ts create mode 100644 packages/api/src/crm/deal/services/close/types.ts create mode 100644 packages/api/src/crm/engagement/services/close/index.ts create mode 100644 packages/api/src/crm/engagement/services/close/mappers.ts create mode 100644 packages/api/src/crm/engagement/services/close/types.ts create mode 100644 packages/api/src/crm/note/services/close/index.ts create mode 100644 packages/api/src/crm/note/services/close/mappers.ts create mode 100644 packages/api/src/crm/note/services/close/types.ts create mode 100644 packages/api/src/crm/stage/services/close/index.ts create mode 100644 packages/api/src/crm/stage/services/close/mappers.ts create mode 100644 packages/api/src/crm/stage/services/close/types.ts create mode 100644 packages/api/src/crm/task/services/close/index.ts create mode 100644 packages/api/src/crm/task/services/close/mappers.ts create mode 100644 packages/api/src/crm/task/services/close/types.ts create mode 100644 packages/api/src/crm/user/services/close/index.ts create mode 100644 packages/api/src/crm/user/services/close/mappers.ts create mode 100644 packages/api/src/crm/user/services/close/types.ts diff --git a/.env.example b/.env.example index 2e505989f..d72e34eb3 100644 --- a/.env.example +++ b/.env.example @@ -54,6 +54,11 @@ FRESHSALES_CRM_CLOUD_CLIENT_SECRET= # Attio ATTIO_CRM_CLOUD_CLIENT_ID= ATTIO_CRM_CLOUD_CLIENT_SECRET= + +# Close +CLOSE_CRM_CLOUD_CLIENT_ID= +CLOSE_CRM_CLOUD_CLIENT_SECRET= + # ================================================ # Ticketing # ================================================ diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6e340f247..62f2510d2 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -188,18 +188,18 @@ services: volumes: - .:/app - ngrok: - image: ngrok/ngrok:latest - restart: always - command: - - "start" - - "--all" - - "--config" - - "/etc/ngrok.yml" - volumes: - - ./ngrok.yml:/etc/ngrok.yml - ports: - - 4040:4040 + #ngrok: + #image: ngrok/ngrok:latest + #restart: always + #command: + # - "start" + # - "--all" + # - "--config" + # - "/etc/ngrok.yml" + #volumes: + #- ./ngrok.yml:/etc/ngrok.yml + #ports: + #- 4040:4040 docs: build: diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 0b92b8aad..c957f3681 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -439,6 +439,8 @@ model projects { linked_users linked_users[] users users @relation(fields: [id_user], references: [id_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_46_1") connector_sets connector_sets @relation(fields: [id_connector_set], references: [id_connector_set], onDelete: NoAction, onUpdate: NoAction, map: "fk_project_connectorsetid") + + @@index([id_connector_set], map: "fk_connectors_sets") } /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments @@ -648,6 +650,7 @@ model connector_sets { crm_zoho Boolean crm_attio Boolean crm_pipedrive Boolean + crm_close Boolean tcg_zendesk Boolean tcg_jira Boolean tcg_gorgias Boolean @@ -668,3 +671,63 @@ model managed_webhooks { modified_at DateTime @db.Timestamp(6) created_at DateTime @db.Timestamp(6) } + +model fs_drives { + id_fs_drive String @id(map: "pk_fs_drives") @db.Uuid + remote_created_at DateTime? @db.Timestamp(6) + drive_url String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + remote_id String? +} + +model fs_files { + id_fs_file String @id(map: "pk_fs_files") @db.Uuid + name String? + type String? + path String? + mime_type String? + size BigInt? + remote_id String? + id_fs_folder String? @db.Uuid + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_fs_permission String @db.Uuid + + @@index([id_fs_folder], map: "fk_fs_file_folderid") + @@index([id_fs_permission], map: "fk_fs_file_permissionid") +} + +model fs_folders { + id_fs_folder String @id(map: "pk_fs_folders") @db.Uuid + folder_url String? + size BigInt? + description String? + parent_folder String? @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_fs_drive String? @db.Uuid + id_fs_permission String @db.Uuid + + @@index([id_fs_drive], map: "fk_fs_folder_driveid") + @@index([id_fs_permission], map: "fk_fs_folder_permissionid") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model fs_permissions { + id_fs_permission String @id(map: "pk_fs_permissions") @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + user String @db.Uuid + group String @db.Uuid + type String[] + roles String[] +} + +model fs_shared_links { + id_fs_shared_link String @id(map: "pk_fs_shared_links") @db.Uuid + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) +} diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index d6a8aa9f1..09ed47f21 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -394,6 +394,7 @@ CREATE TABLE connector_sets crm_zoho boolean NOT NULL, crm_attio boolean NOT NULL, crm_pipedrive boolean NOT NULL, + crm_close boolean NOT NULL, tcg_zendesk boolean NOT NULL, tcg_jira boolean NOT NULL, tcg_gorgias boolean NOT NULL, diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index bc95a73fc..1dddd2fc4 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pool', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 7260a3aab..5c16d7ca2 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -113,12 +113,12 @@ export class ConnectionsController { break; } // Performing Core Sync Service - this.coreSyncService.initialSync( + /*this.coreSyncService.initialSync( vertical.toLowerCase(), providerName, linkedUserId, projectId, - ); + );*/ res.redirect(returnUrl); } catch (error) { handleServiceError(error, this.logger); diff --git a/packages/api/src/@core/connections/crm/crm.connection.module.ts b/packages/api/src/@core/connections/crm/crm.connection.module.ts index 9dc79933d..42d8b537c 100644 --- a/packages/api/src/@core/connections/crm/crm.connection.module.ts +++ b/packages/api/src/@core/connections/crm/crm.connection.module.ts @@ -13,6 +13,7 @@ import { ZendeskConnectionService } from './services/zendesk/zendesk.service'; import { PipedriveConnectionService } from './services/pipedrive/pipedrive.service'; import { AttioConnectionService } from './services/attio/attio.service'; import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { CloseConnectionService } from './services/close/close.service'; @Module({ imports: [WebhookModule], @@ -31,6 +32,7 @@ import { ConnectionsStrategiesService } from '@@core/connections-strategies/conn ZohoConnectionService, ZendeskConnectionService, PipedriveConnectionService, + CloseConnectionService, ], exports: [CrmConnectionsService], }) diff --git a/packages/api/src/@core/connections/crm/services/close/close.service.ts b/packages/api/src/@core/connections/crm/services/close/close.service.ts index 89469aebf..c71e2ebb5 100644 --- a/packages/api/src/@core/connections/crm/services/close/close.service.ts +++ b/packages/api/src/@core/connections/crm/services/close/close.service.ts @@ -85,10 +85,7 @@ export class CloseConnectionService implements ICrmConnectionService { }, ); const data: CloseOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : close ticketing ' + JSON.stringify(data), - ); - + this.logger.log('OAuth credentials : close CRM ' + JSON.stringify(data)); let db_res; const connection_token = uuidv4(); @@ -100,7 +97,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, expiration_timestamp: new Date( new Date().getTime() + Number(data.expires_in) * 1000, ), @@ -153,10 +150,10 @@ export class CloseConnectionService implements ICrmConnectionService { )) as OAuth2AuthData; const formData = new URLSearchParams({ - grant_type: 'refresh_token', refresh_token: this.cryptoService.decrypt(refreshToken), client_id: CREDENTIALS.CLIENT_ID, client_secret: CREDENTIALS.CLIENT_SECRET, + grant_type: 'refresh_token', }); const res = await axios.post( 'https://api.close.com/oauth2/token', @@ -168,18 +165,21 @@ export class CloseConnectionService implements ICrmConnectionService { }, ); const data: CloseOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); + if (res?.data?.access_token) { + //only update when it is successful + await this.prisma.connections.update({ + where: { + id_connection: connectionId, + }, + data: { + access_token: this.cryptoService.encrypt(data?.access_token), + refresh_token: this.cryptoService.encrypt(data?.refresh_token), + expiration_timestamp: new Date( + new Date().getTime() + Number(data?.expires_in) * 1000, + ), + }, + }); + } this.logger.log('OAuth credentials updated : close '); } catch (error) { handleServiceError(error, this.logger, 'close', Action.oauthRefresh); diff --git a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts index 455a97526..6ab2f648c 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts @@ -91,27 +91,32 @@ export class JiraConnectionService implements ITicketingConnectionService { // get the cloud id from atlassian jira, it is used across requests to the api //TODO: add a field inside our connections db to handle it - const res_ = await axios.post( + const res_ = await axios.get( `https://api.atlassian.com/oauth/token/accessible-resources`, - formData.toString(), { headers: { Authorization: `Bearer ${data.access_token}`, - 'Content-Type': 'application/json', + Accept: 'application/json', }, }, ); const sites_scopes: JiraCloudIdInformation[] = res_.data; - let cloud_id: string; - for (const site of sites_scopes) { - if (site.url == 'https://panora.atlassian.net') { - cloud_id = site.id; - break; - } - } + this.logger.log( + 'sites scopes for jira are ----> ' + JSON.stringify(sites_scopes), + ); + const cloud_id: string = sites_scopes[0].id; //todo let db_res; const connection_token = uuidv4(); + const access_token = this.cryptoService.encrypt(data.access_token); + this.logger.log( + 'non-encrypted token is ----> ' + JSON.stringify(data.access_token), + ); + + this.logger.log( + 'encrypted token is ----> ' + JSON.stringify(access_token), + ); + if (isNotUnique) { db_res = await this.prisma.connections.update({ where: { @@ -180,7 +185,7 @@ export class JiraConnectionService implements ITicketingConnectionService { formData.toString(), { headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + 'Content-Type': 'application/json', Authorization: `Basic ${Buffer.from( `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, ).toString('base64')}`, diff --git a/packages/api/src/@core/encryption/encryption.service.ts b/packages/api/src/@core/encryption/encryption.service.ts index 1707b7a2a..c2e4a2559 100644 --- a/packages/api/src/@core/encryption/encryption.service.ts +++ b/packages/api/src/@core/encryption/encryption.service.ts @@ -11,7 +11,7 @@ export class EncryptionService { this.secretKey = this.env.getCryptoKey(); } - encrypt(data: string): string { + encrypt(data: string) { try { const cipher = crypto.createCipheriv( 'aes-256-cbc', diff --git a/packages/api/src/@core/project-connectors/project-connectors.controller.ts b/packages/api/src/@core/project-connectors/project-connectors.controller.ts index 033b48441..748c92610 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.controller.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.controller.ts @@ -30,6 +30,7 @@ export interface TypeCustom { tcg_front: boolean; tcg_jira: boolean; tcg_gitlab: boolean; + crm_close: boolean; } @ApiTags('project-connectors') @Controller('project-connectors') diff --git a/packages/api/src/@core/project-connectors/project-connectors.service.ts b/packages/api/src/@core/project-connectors/project-connectors.service.ts index 067d9a892..394c03fca 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.service.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.service.ts @@ -67,6 +67,7 @@ export class ProjectConnectorsService { tcg_front: data.tcg_front, tcg_jira: data.tcg_jira, tcg_gitlab: data.tcg_gitlab, + crm_close: data.crm_close, }; const res = await this.prisma.connector_sets.create({ diff --git a/packages/api/src/@core/utils/errors.ts b/packages/api/src/@core/utils/errors.ts index 628d14ea9..e32281d98 100644 --- a/packages/api/src/@core/utils/errors.ts +++ b/packages/api/src/@core/utils/errors.ts @@ -76,18 +76,16 @@ export function handleServiceError( } else if (error instanceof Prisma.PrismaClientKnownRequestError) { // Handle Prisma errors logger.error('Error with Prisma request:', errorMessage); + throw new Error(errorMessage); } else { logger.error('An unknown error occurred...', errorMessage); + throw new Error(errorMessage); } if (error instanceof HttpException) { throw error; } - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new HttpException(errorMessage, statusCode); - } - return { data: null, error: errorMessage, diff --git a/packages/api/src/@core/utils/types/original/original.crm.ts b/packages/api/src/@core/utils/types/original/original.crm.ts index fee9b6d29..3f06b1c10 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -3,6 +3,7 @@ import { HubspotCompanyOutput } from '@crm/company/services/hubspot/types'; import { PipedriveCompanyOutput } from '@crm/company/services/pipedrive/types'; import { ZendeskCompanyOutput } from '@crm/company/services/zendesk/types'; import { ZohoCompanyOutput } from '@crm/company/services/zoho/types'; +import { CloseCompanyOutput } from '@crm/company/services/close/types'; import { AttioContactInput, AttioContactOutput, @@ -19,10 +20,15 @@ import { ZohoContactInput, ZohoContactOutput, } from '@crm/contact/services/zoho/types'; +import { + CloseContactInput, + CloseContactOutput, +} from '@crm/contact/services/close/types'; import { HubspotDealOutput } from '@crm/deal/services/hubspot/types'; import { PipedriveDealOutput } from '@crm/deal/services/pipedrive/types'; import { ZendeskDealOutput } from '@crm/deal/services/zendesk/types'; import { ZohoDealOutput } from '@crm/deal/services/zoho/types'; +import { CloseDealOutput } from '@crm/deal/services/close/types'; import { HubspotEngagementInput, HubspotEngagementOutput, @@ -39,6 +45,10 @@ import { ZohoEngagementInput, ZohoEngagementOutput, } from '@crm/engagement/services/zoho/types'; +import { + CloseEngagementInput, + CloseEngagementOutput, +} from '@crm/engagement/services/close/types'; import { HubspotNoteInput, HubspotNoteOutput, @@ -52,6 +62,10 @@ import { ZendeskNoteOutput, } from '@crm/note/services/zendesk/types'; import { ZohoNoteInput, ZohoNoteOutput } from '@crm/note/services/zoho/types'; +import { + CloseNoteInput, + CloseNoteOutput, +} from '@crm/note/services/close/types'; import { HubspotStageInput, HubspotStageOutput, @@ -68,6 +82,10 @@ import { ZohoStageInput, ZohoStageOutput, } from '@crm/stage/services/zoho/types'; +import { + CloseStageInput, + CloseStageOutput, +} from '@crm/stage/services/close/types'; import { HubspotTaskInput, HubspotTaskOutput, @@ -81,6 +99,10 @@ import { ZendeskTaskOutput, } from '@crm/task/services/zendesk/types'; import { ZohoTaskInput, ZohoTaskOutput } from '@crm/task/services/zoho/types'; +import { + CloseTaskInput, + CloseTaskOutput, +} from '@crm/task/services/close/types'; import { HubspotUserInput, HubspotUserOutput, @@ -90,6 +112,10 @@ import { PipedriveUserOutput, } from '@crm/user/services/pipedrive/types'; import { ZohoUserInput, ZohoUserOutput } from '@crm/user/services/zoho/types'; +import { + CloseUserInput, + CloseUserOutput, +} from '@crm/user/services/close/types'; import { ZendeskContactInput, ZendeskContactOutput, @@ -107,14 +133,16 @@ export type OriginalContactInput = | ZohoContactInput | ZendeskContactInput | PipedriveContactInput - | AttioContactInput; + | AttioContactInput + | CloseContactInput; /* deal */ export type OriginalDealInput = | HubspotDealOutput | ZohoDealOutput | ZendeskDealOutput - | PipedriveDealOutput; + | PipedriveDealOutput + | CloseDealOutput; /* company */ export type OriginalCompanyInput = @@ -122,35 +150,40 @@ export type OriginalCompanyInput = | ZohoCompanyOutput | ZendeskCompanyOutput | PipedriveCompanyOutput - | AttioCompanyOutput; + | AttioCompanyOutput + | CloseCompanyOutput; /* engagement */ export type OriginalEngagementInput = | HubspotEngagementInput | ZohoEngagementInput | ZendeskEngagementInput - | PipedriveEngagementInput; + | PipedriveEngagementInput + | CloseEngagementInput; /* note */ export type OriginalNoteInput = | HubspotNoteInput | ZohoNoteInput | ZendeskNoteInput - | PipedriveNoteInput; + | PipedriveNoteInput + | CloseNoteInput; /* task */ export type OriginalTaskInput = | HubspotTaskInput | ZohoTaskInput | ZendeskTaskInput - | PipedriveTaskInput; + | PipedriveTaskInput + | CloseTaskInput; /* stage */ export type OriginalStageInput = | HubspotStageInput | ZohoStageInput | ZendeskStageInput - | PipedriveStageInput; + | PipedriveStageInput + | CloseStageInput; /* engagementType */ @@ -159,7 +192,8 @@ export type OriginalUserInput = | HubspotUserInput | ZohoUserInput | ZendeskUserInput - | PipedriveUserInput; + | PipedriveUserInput + | CloseUserOutput; export type CrmObjectInput = | OriginalContactInput @@ -178,14 +212,16 @@ export type OriginalContactOutput = | ZohoContactOutput | ZendeskContactOutput | PipedriveContactOutput - | AttioContactOutput; + | AttioContactOutput + | CloseContactOutput; /* deal */ export type OriginalDealOutput = | HubspotDealOutput | ZohoDealOutput | ZendeskDealOutput - | PipedriveDealOutput; + | PipedriveDealOutput + | CloseDealOutput; /* company */ export type OriginalCompanyOutput = @@ -193,35 +229,40 @@ export type OriginalCompanyOutput = | ZohoCompanyOutput | ZendeskCompanyOutput | PipedriveCompanyOutput - | AttioCompanyOutput; + | AttioCompanyOutput + | CloseCompanyOutput; /* engagement */ export type OriginalEngagementOutput = | HubspotEngagementOutput | ZohoEngagementOutput | ZendeskEngagementOutput - | PipedriveEngagementOutput; + | PipedriveEngagementOutput + | CloseEngagementOutput; /* note */ export type OriginalNoteOutput = | HubspotNoteOutput | ZohoNoteOutput | ZendeskNoteOutput - | PipedriveNoteOutput; + | PipedriveNoteOutput + | CloseNoteOutput; /* task */ export type OriginalTaskOutput = | HubspotTaskOutput | ZohoTaskOutput | ZendeskTaskOutput - | PipedriveTaskOutput; + | PipedriveTaskOutput + | CloseTaskOutput; /* stage */ export type OriginalStageOutput = | HubspotStageOutput | ZohoStageOutput | ZendeskStageOutput - | PipedriveStageOutput; + | PipedriveStageOutput + | CloseStageOutput; /* engagementType */ @@ -230,7 +271,8 @@ export type OriginalUserOutput = | HubspotUserOutput | ZohoUserOutput | ZendeskUserOutput - | PipedriveUserOutput; + | PipedriveUserOutput + | CloseUserInput; export type CrmObjectOutput = | OriginalContactOutput diff --git a/packages/api/src/crm/@lib/@types/index.ts b/packages/api/src/crm/@lib/@types/index.ts index 6cd6cad51..cc1f45e32 100644 --- a/packages/api/src/crm/@lib/@types/index.ts +++ b/packages/api/src/crm/@lib/@types/index.ts @@ -415,7 +415,7 @@ export class Address { @ApiProperty({ type: String, - description: 'The country.', + description: 'The country', }) @IsString() country: string; diff --git a/packages/api/src/crm/company/company.module.ts b/packages/api/src/crm/company/company.module.ts index 3c6450280..141521f89 100644 --- a/packages/api/src/crm/company/company.module.ts +++ b/packages/api/src/crm/company/company.module.ts @@ -14,6 +14,7 @@ import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; import { AttioService } from './services/attio'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { AttioService } from './services/attio'; PipedriveService, HubspotService, AttioService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/company/services/close/index.ts b/packages/api/src/crm/company/services/close/index.ts new file mode 100644 index 000000000..8c87d3591 --- /dev/null +++ b/packages/api/src/crm/company/services/close/index.ts @@ -0,0 +1,117 @@ +import { Injectable } from '@nestjs/common'; +import { ICompanyService } from '@crm/company/types'; +import { CrmObject } from '@crm/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { + commonCompanyCloseProperties, + CloseCompanyInput, + CloseCompanyOutput, +} from './types'; + +@Injectable() +export class CloseService implements ICompanyService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.company.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addCompany( + companyData: CloseCompanyInput, + linkedUserId: string, + ): Promise> { + 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> { + 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, + ); + } + } +} diff --git a/packages/api/src/crm/company/services/close/mappers.ts b/packages/api/src/crm/company/services/close/mappers.ts new file mode 100644 index 000000000..026c01879 --- /dev/null +++ b/packages/api/src/crm/company/services/close/mappers.ts @@ -0,0 +1,111 @@ +import { CloseCompanyInput, CloseCompanyOutput } from './types'; +import { + UnifiedCompanyInput, + UnifiedCompanyOutput, +} from '@crm/company/types/model.unified'; +import { ICompanyMapper } from '@crm/company/types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseCompanyMapper implements ICompanyMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedCompanyInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: CloseCompanyInput = { + name: source?.name, + addresses: source?.addresses?.map((address) => ({ + address_1: address.street_1, + address_2: address.street_2, + city: address.city, + state: address.state, + zipcode: address.postal_code, + label: address.address_type, + })) as CloseCompanyInput['addresses'], + }; + + 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 unify( + source: CloseCompanyOutput | CloseCompanyOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleCompanyToUnified(source, customFieldMappings); + } + // Handling array of CloseCompanyOutput + return Promise.all( + source.map((company) => + this.mapSingleCompanyToUnified(company, customFieldMappings), + ), + ); + } + + private async mapSingleCompanyToUnified( + company: CloseCompanyOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = company[mapping.remote_id]; + } + } + let opts: any = {}; + if (company?.created_by || company?.custom?.close_owner_id) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + (company?.created_by || company?.custom?.close_owner_id) as string, + 'close', + ); + if (owner_id) { + opts = { + user_id: owner_id, + }; + } + } + return { + remote_id: company.id, + name: company.name, + industry: company?.custom?.Industry || '', + number_of_employees: company?.custom?.employees || 0, // Placeholder, as there's no direct mapping provided + addresses: company?.addresses?.map((address) => ({ + street_1: address.address_1, + street_2: address.address_2, + city: address.city, + state: address.state, + postal_code: address.zipcode, + country: address.country, + address_type: address.label, + owner_type: 'company', + })), // Assuming 'street', 'city', 'state', 'postal_code', 'country' are properties in company.properties + phone_numbers: [], + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/company/services/close/types.ts b/packages/api/src/crm/company/services/close/types.ts new file mode 100644 index 000000000..fb515f762 --- /dev/null +++ b/packages/api/src/crm/company/services/close/types.ts @@ -0,0 +1,133 @@ +interface Email { + type: string; + email: string; +} + +interface Phone { + type: string; + phone: string; +} + +interface Contact { + name: string; + title: string; + emails: Email[]; + phones: Phone[]; +} + +interface Address { + label: string; + address_1: string; + address_2: string; + city: string; + state: string; + zipcode: string; + country: string; +} + +interface CustomFields { + [key: string]: string | string[]; +} + +interface CompanyInput { + name: string; + url: string; + description: string; + status_id: string; + contacts: Contact[]; + custom: CustomFields; + addresses: Address[]; + status?: string; +} + +export type CloseCompanyInput = Partial; + +interface Phone { + phone: string; + phone_formatted: string; + type: string; +} + +interface Email { + type: string; + email: string; + is_unsubscribed: boolean; +} + +interface Contact { + name: string; + title: string; + date_updated: string; + phones: Phone[]; + custom: { + [key: string]: string; + }; + created_by: string | null; + id: string; + organization_id: string; + date_created: string; + emails: Email[]; + updated_by: string; +} + +interface Opportunity { + status_id: string; + status_label: string; + status_type: string; + pipeline_id: string; + pipeline_name: string; + date_won: string | null; + confidence: number; + user_id: string; + contact_id: string | null; + updated_by: string | null; + date_updated: string; + value_period: string; + created_by: string | null; + note: string; + value: number; + value_formatted: string; + value_currency: string; + lead_name: string; + organization_id: string; + date_created: string; + user_name: string; + id: string; + lead_id: string; +} + +interface Lead { + status_id: string; + status_label: string; + tasks: any[]; + display_name: string; + addresses: Partial
[]; + name: string; + contacts: Contact[]; + custom: { + [key: string]: string | string[]; + }; + date_updated: string; + html_url: string; + created_by: string | null; + organization_id: string; + url: string | null; + opportunities: Opportunity[]; + updated_by: string; + date_created: string; + id: string; + description: string; +} + +export type CloseCompanyOutput = Partial; + +export const commonCompanyCloseProperties = { + city: '', + createdate: '', + domain: '', + industry: '', + name: '', + phone: '', + state: '', + close_owner_id: '', +}; diff --git a/packages/api/src/crm/company/types/mappingsTypes.ts b/packages/api/src/crm/company/types/mappingsTypes.ts index 660920a6a..d394f8bb8 100644 --- a/packages/api/src/crm/company/types/mappingsTypes.ts +++ b/packages/api/src/crm/company/types/mappingsTypes.ts @@ -3,12 +3,14 @@ import { HubspotCompanyMapper } from '../services/hubspot/mappers'; import { PipedriveCompanyMapper } from '../services/pipedrive/mappers'; import { ZendeskCompanyMapper } from '../services/zendesk/mappers'; import { ZohoCompanyMapper } from '../services/zoho/mappers'; +import { CloseCompanyMapper } from '../services/close/mappers'; const hubspotCompanyMapper = new HubspotCompanyMapper(); const zendeskCompanyMapper = new ZendeskCompanyMapper(); const zohoCompanyMapper = new ZohoCompanyMapper(); const pipedriveCompanyMapper = new PipedriveCompanyMapper(); const attioCompanyMapper = new AttioCompanyMapper(); +const closeCompanyMapper = new CloseCompanyMapper(); export const companyUnificationMapping = { hubspot: { @@ -31,4 +33,8 @@ export const companyUnificationMapping = { unify: attioCompanyMapper.unify.bind(attioCompanyMapper), desunify: attioCompanyMapper.desunify.bind(attioCompanyMapper), }, + close: { + unify: closeCompanyMapper.unify.bind(closeCompanyMapper), + desunify: closeCompanyMapper.desunify.bind(closeCompanyMapper), + }, }; diff --git a/packages/api/src/crm/contact/contact.module.ts b/packages/api/src/crm/contact/contact.module.ts index 5a3de9885..d592fe7d0 100644 --- a/packages/api/src/crm/contact/contact.module.ts +++ b/packages/api/src/crm/contact/contact.module.ts @@ -14,6 +14,7 @@ import { WebhookService } from '@@core/webhook/webhook.service'; import { BullModule } from '@nestjs/bull'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from './services/registry.service'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -40,6 +41,7 @@ import { ServiceRegistry } from './services/registry.service'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/contact/services/close/index.ts b/packages/api/src/crm/contact/services/close/index.ts new file mode 100644 index 000000000..579d6bd84 --- /dev/null +++ b/packages/api/src/crm/contact/services/close/index.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@nestjs/common'; +import { IContactService } from '@crm/contact/types'; +import { CrmObject } from '@crm/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { CloseContactInput, CloseContactOutput } from './types'; + +@Injectable() +export class CloseService implements IContactService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.contact.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addContact( + contactData: CloseContactInput, + linkedUserId: string, + ): Promise> { + 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}/contact`, + JSON.stringify(contactData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close contact created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.contact, + ActionType.POST, + ); + } + } + + async syncContacts( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/contact`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close contacts !`); + + return { + data: resp?.data?.data, + message: 'Close contacts retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.contact, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/contact/services/close/mappers.ts b/packages/api/src/crm/contact/services/close/mappers.ts new file mode 100644 index 000000000..1f5f39e8b --- /dev/null +++ b/packages/api/src/crm/contact/services/close/mappers.ts @@ -0,0 +1,111 @@ +import { + UnifiedContactInput, + UnifiedContactOutput, +} from '@crm/contact/types/model.unified'; +import { IContactMapper } from '@crm/contact/types'; +import { + CloseContactInput, + CloseContactOutput, + InputPhone, + InputEmail, +} from './types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseContactMapper implements IContactMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedContactInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + // 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 unify( + source: CloseContactOutput | CloseContactOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleContactToUnified(source, customFieldMappings); + } + // Handling array of CloseContactOutput + return source.map((contact) => + this.mapSingleContactToUnified(contact, customFieldMappings), + ); + } + + private mapSingleContactToUnified( + contact: CloseContactOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedContactOutput { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = contact[mapping.remote_id]; + } + } + + return { + remote_id: contact.id, + first_name: contact.name, + last_name: '', + email_addresses: contact.emails?.map(({ email, type }) => ({ + email_address: email, + email_address_type: type, + owner_type: 'contact', + })), + phone_numbers: contact.phones?.map(({ phone, type }) => ({ + phone_number: phone, + phone_type: type, + owner_type: 'contact', + })), + field_mappings, + addresses: [], + }; + } +} diff --git a/packages/api/src/crm/contact/services/close/types.ts b/packages/api/src/crm/contact/services/close/types.ts new file mode 100644 index 000000000..a1d937600 --- /dev/null +++ b/packages/api/src/crm/contact/services/close/types.ts @@ -0,0 +1,69 @@ +export interface InputPhone { + phone: string; + type: string; +} + +export interface InputEmail { + email: string; + type: string; +} + +interface Url { + url: string; + type: string; +} + +interface CustomFields { + [key: string]: string; // Allows dynamic keys for custom fields +} + +interface LeadInput { + lead_id: string; + name: string; + title: string; + phones: InputPhone[]; + emails: InputEmail[]; + urls: Url[]; + custom?: CustomFields; +} + +interface Phone { + country: string; + phone: string; + phone_formatted: string; + type: string; +} + +interface Email { + type: string; + email: string; + is_unsubscribed: boolean; +} + +interface Contact { + id: string; + organization_id: string; + lead_id: string; + name: string; + title: string; + phones: Phone[]; + emails: Email[]; + date_created: string; + date_updated: string; + created_by: string; + updated_by: string; +} + +export type CloseContactInput = Partial; + +export type CloseContactOutput = Partial; + +export const commonCloseProperties = { + createdate: '', + email: '', + firstname: '', + hs_object_id: '', + lastmodifieddate: '', + lastname: '', + close_owner_id: '', +}; diff --git a/packages/api/src/crm/contact/types/mappingsTypes.ts b/packages/api/src/crm/contact/types/mappingsTypes.ts index 46d3b57d5..d1cce1878 100644 --- a/packages/api/src/crm/contact/types/mappingsTypes.ts +++ b/packages/api/src/crm/contact/types/mappingsTypes.ts @@ -3,12 +3,14 @@ import { HubspotContactMapper } from '../services/hubspot/mappers'; import { PipedriveContactMapper } from '../services/pipedrive/mappers'; import { ZendeskContactMapper } from '../services/zendesk/mappers'; import { ZohoContactMapper } from '../services/zoho/mappers'; +import { CloseContactMapper } from '../services/close/mappers'; const hubspotContactMapper = new HubspotContactMapper(); const zendeskContactMapper = new ZendeskContactMapper(); const zohoContactMapper = new ZohoContactMapper(); const pipedriveContactMapper = new PipedriveContactMapper(); const attioContactMapper = new AttioContactMapper(); +const closeContactMapper = new CloseContactMapper(); export const contactUnificationMapping = { hubspot: { @@ -31,4 +33,8 @@ export const contactUnificationMapping = { unify: attioContactMapper.unify.bind(attioContactMapper), desunify: attioContactMapper.desunify.bind(attioContactMapper), }, + close: { + unify: closeContactMapper.unify.bind(closeContactMapper), + desunify: closeContactMapper.desunify.bind(closeContactMapper), + }, }; diff --git a/packages/api/src/crm/deal/deal.module.ts b/packages/api/src/crm/deal/deal.module.ts index ba2f66537..e13d5cdf8 100644 --- a/packages/api/src/crm/deal/deal.module.ts +++ b/packages/api/src/crm/deal/deal.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/deal/services/close/index.ts b/packages/api/src/crm/deal/services/close/index.ts new file mode 100644 index 000000000..36afb0dc4 --- /dev/null +++ b/packages/api/src/crm/deal/services/close/index.ts @@ -0,0 +1,106 @@ +import { Injectable } from '@nestjs/common'; +import { IDealService } from '@crm/deal/types'; +import { CrmObject } from '@crm/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { CloseDealInput, CloseDealOutput } from './types'; +@Injectable() +export class CloseService implements IDealService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.deal.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addDeal( + dealData: CloseDealInput, + linkedUserId: string, + ): Promise> { + 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, + ); + } + } + + async syncDeals( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + //crm.schemas.deals.read","crm.objects.deals.read + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/opportunity/`; + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close deals !`); + + return { + data: resp?.data?.data, + message: 'Close deals retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.deal, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/deal/services/close/mappers.ts b/packages/api/src/crm/deal/services/close/mappers.ts new file mode 100644 index 000000000..2fd716778 --- /dev/null +++ b/packages/api/src/crm/deal/services/close/mappers.ts @@ -0,0 +1,141 @@ +import { CloseDealInput, CloseDealOutput } from './types'; +import { + UnifiedDealInput, + UnifiedDealOutput, +} from '@crm/deal/types/model.unified'; +import { IDealMapper } from '@crm/deal/types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseDealMapper implements IDealMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedDealInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: CloseDealInput = { + note: source.description, + confidence: 0, + value: source.amount || 0, + value_period: 'one_time', + custom: {}, + lead_id: '', + }; + + if (source.company_id) { + const lead_id = await this.utils.getRemoteIdFromCompanyUuid( + source.company_id, + ); + if (lead_id) { + result.lead_id = lead_id; + } + } + if (source.stage_id) { + const stage_id = await this.utils.getStageIdFromStageUuid( + source.company_id, + ); + if (stage_id) { + result.status_id = stage_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 unify( + source: CloseDealOutput | CloseDealOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleDealToUnified(source, customFieldMappings); + } + // Handling array of CloseDealOutput + return Promise.all( + source.map((deal) => + this.mapSingleDealToUnified(deal, customFieldMappings), + ), + ); + } + + private async mapSingleDealToUnified( + deal: CloseDealOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = deal.custom[mapping.remote_id]; + } + } + + let opts: any = {}; + if (deal.user_id) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + deal.user_id, + 'close', + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + if (deal.lead_id) { + const lead_id = await this.utils.getCompanyUuidFromRemoteId( + deal.lead_id, + 'close', + ); + if (lead_id) { + opts = { + ...opts, + company_id: lead_id, + }; + } + } + if (deal.contact_id) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + deal.contact_id, + 'close', + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id, + }; + } + } + return { + remote_id: deal.id, + name: deal.note, + description: deal.note, // Placeholder if there's no direct mapping + amount: parseFloat(`${deal.value || 0}`), + //TODO; stage_id: deal.properties.dealstage, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/deal/services/close/types.ts b/packages/api/src/crm/deal/services/close/types.ts new file mode 100644 index 000000000..45e7a4cea --- /dev/null +++ b/packages/api/src/crm/deal/services/close/types.ts @@ -0,0 +1,45 @@ +interface CustomFields { + [key: string]: string; +} + +export interface CloseDealInput { + note?: string; + confidence?: number; + lead_id: string; + status_id?: string; + value?: number; + value_period?: string; + custom?: CustomFields; +} + +interface Opportunity { + id: string; + organization_id: string; + lead_id: string; + lead_name: string; + status_id: string; + status_label: string; + status_type: string; + pipeline_id: string; + pipeline_name: string; + value: number; + value_period: string; + value_formatted: string; + value_currency: string; + expected_value: number; + annualized_value: number; + annualized_expected_value: number; + date_won: string | null; + confidence: number; + note: string; + user_id: string; + user_name: string; + contact_id: string | null; + created_by: string; + updated_by: string; + date_updated: string; + date_created: string; + custom: CustomFields; +} + +export type CloseDealOutput = Partial; diff --git a/packages/api/src/crm/deal/types/mappingsTypes.ts b/packages/api/src/crm/deal/types/mappingsTypes.ts index c665474e4..729c27586 100644 --- a/packages/api/src/crm/deal/types/mappingsTypes.ts +++ b/packages/api/src/crm/deal/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotDealMapper } from '../services/hubspot/mappers'; import { PipedriveDealMapper } from '../services/pipedrive/mappers'; import { ZendeskDealMapper } from '../services/zendesk/mappers'; import { ZohoDealMapper } from '../services/zoho/mappers'; +import { CloseDealMapper } from '../services/close/mappers'; const hubspotDealMapper = new HubspotDealMapper(); const zendeskDealMapper = new ZendeskDealMapper(); const zohoDealMapper = new ZohoDealMapper(); const pipedriveDealMapper = new PipedriveDealMapper(); +const closeDealMapper = new CloseDealMapper(); export const dealUnificationMapping = { hubspot: { @@ -25,4 +27,8 @@ export const dealUnificationMapping = { unify: zendeskDealMapper.unify.bind(zendeskDealMapper), desunify: zendeskDealMapper.desunify.bind(zendeskDealMapper), }, + close: { + unify: closeDealMapper.unify.bind(closeDealMapper), + desunify: closeDealMapper.desunify.bind(closeDealMapper), + }, }; diff --git a/packages/api/src/crm/engagement/engagement.module.ts b/packages/api/src/crm/engagement/engagement.module.ts index 1f068bf0e..40c3ebd88 100644 --- a/packages/api/src/crm/engagement/engagement.module.ts +++ b/packages/api/src/crm/engagement/engagement.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/engagement/services/close/index.ts b/packages/api/src/crm/engagement/services/close/index.ts new file mode 100644 index 000000000..7662a3e73 --- /dev/null +++ b/packages/api/src/crm/engagement/services/close/index.ts @@ -0,0 +1,334 @@ +import { Injectable } from '@nestjs/common'; +import { IEngagementService } from '@crm/engagement/types'; +import { CrmObject } from '@crm/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { + CloseEngagementCallInput, + CloseEngagementCallOutput, + CloseEngagementEmailInput, + CloseEngagementEmailOutput, + CloseEngagementInput, + CloseEngagementMeetingInput, + CloseEngagementMeetingOutput, + CloseEngagementOutput, +} from './types'; + +@Injectable() +export class CloseService implements IEngagementService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.engagement.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addEngagement( + engagementData: CloseEngagementInput, + linkedUserId: string, + engagement_type: string, + ): Promise> { + try { + switch (engagement_type) { + case 'CALL': + return this.addCall( + engagementData as CloseEngagementCallInput, + linkedUserId, + ); + case 'MEETING': + return this.addMeeting( + engagementData as CloseEngagementMeetingInput, + linkedUserId, + ); + case 'EMAIL': + return this.addEmail( + engagementData as CloseEngagementEmailInput, + linkedUserId, + ); + default: + break; + } + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement, + ActionType.POST, + ); + } + } + + private async addCall( + engagementData: CloseEngagementCallInput, + linkedUserId: string, + ): Promise> { + 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}/activity/call`, + JSON.stringify(engagementData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close call created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_call, + ActionType.POST, + ); + } + } + + private async addMeeting( + engagementData: CloseEngagementMeetingInput, + linkedUserId: string, + ): Promise> { + 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}/activity/meeting`, + JSON.stringify(engagementData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close meeting created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_meeting, + ActionType.POST, + ); + } + } + + private async addEmail( + engagementData: CloseEngagementEmailInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + const dataBody = { + properties: engagementData, + }; + const resp = await axios.post( + `${connection.account_url}/activity/email`, + JSON.stringify(dataBody), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close email created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_email, + ActionType.POST, + ); + } + } + + async syncEngagements( + linkedUserId: string, + engagement_type: string, + custom_properties?: string[], + ): Promise> { + try { + switch (engagement_type) { + case 'CALL': + return this.syncCalls(linkedUserId, custom_properties); + case 'MEETING': + return this.syncMeetings(linkedUserId, custom_properties); + case 'EMAIL': + return this.syncEmails(linkedUserId, custom_properties); + default: + break; + } + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement, + ActionType.GET, + ); + } + } + + private async syncCalls(linkedUserId: string, custom_properties?: string[]) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/activity/call`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close engagements calls !`); + + return { + data: resp?.data?.data || [], + message: 'Close engagements calls retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_call, + ActionType.GET, + ); + } + } + + private async syncMeetings( + linkedUserId: string, + custom_properties?: string[], + ) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/activity/meeting`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close engagements meetings !`); + return { + data: resp?.data?.data, + message: 'Close engagements meetings retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_meeting, + ActionType.GET, + ); + } + } + + private async syncEmails(linkedUserId: string, custom_properties?: string[]) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/activity/email`; + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close engagements emails !`); + return { + data: resp?.data?.data, + message: 'Close engagements emails retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.engagement_email, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/engagement/services/close/mappers.ts b/packages/api/src/crm/engagement/services/close/mappers.ts new file mode 100644 index 000000000..751c2b1e5 --- /dev/null +++ b/packages/api/src/crm/engagement/services/close/mappers.ts @@ -0,0 +1,459 @@ +import { + CloseEngagementCallInput, + CloseEngagementCallOutput, + CloseEngagementEmailInput, + CloseEngagementEmailOutput, + CloseEngagementInput, + CloseEngagementMeetingInput, + CloseEngagementMeetingOutput, + CloseEngagementOutput, +} from './types'; +import { + UnifiedEngagementInput, + UnifiedEngagementOutput, +} from '@crm/engagement/types/model.unified'; +import { IEngagementMapper } from '@crm/engagement/types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseEngagementMapper implements IEngagementMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const type = source.type; + switch (type) { + case 'CALL': + return await this.desunifyCall(source, customFieldMappings); + case 'MEETING': + return await this.desunifyMeeting(source, customFieldMappings); + case 'EMAIL': + return await this.desunifyEmail(source, customFieldMappings); + default: + break; + } + return; + } + + private async desunifyCall( + source: UnifiedEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const diffInMilliseconds = + source.start_at && source.end_time + ? new Date(source.end_time).getTime() - + new Date(source.start_at).getTime() + : 0; + const result: CloseEngagementCallInput = { + note_html: source.content || '', + direction: ( + (source.direction === 'OUTBOUND' ? 'outgoing' : source.direction) || '' + ).toLowerCase(), + duration: Math.floor(diffInMilliseconds / (1000 * 60)), + }; + + // Map HubSpot owner ID from user ID + if (source.user_id) { + const owner_id = await this.utils.getRemoteIdFromUserUuid(source.user_id); + if (owner_id) { + result.created_by = owner_id; + result.user_id = owner_id; + } + } + if (source.company_id) { + result.lead_id = await this.utils.getRemoteIdFromCompanyUuid( + source.company_id, + ); + } + if (source?.contacts && source?.contacts?.length) { + const contactId = await this.utils.getRemoteIdFromUserUuid( + source.contacts[0], + ); + if (contactId) { + result.contact_id = contactId; + } + } + + 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; + } + + private async desunifyMeeting( + source: UnifiedEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return {}; + } + + private async desunifyEmail( + source: UnifiedEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: CloseEngagementEmailInput = { + body_text: source.content || '', + status: '', // Placeholder, needs appropriate mapping + sender: '', + to: [], + bcc: [], + cc: [], + direction: ( + (source.direction === 'OUTBOUND' ? 'outgoing' : source.direction) || '' + ).toLowerCase(), + }; + + // Map HubSpot owner ID from user ID + if (source.user_id) { + const owner_id = await this.utils.getRemoteIdFromUserUuid(source.user_id); + if (owner_id) { + result.user_id = owner_id; + } + } + if (source.company_id) { + result.lead_id = await this.utils.getRemoteIdFromCompanyUuid( + source.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 unify( + source: CloseEngagementOutput | CloseEngagementOutput[], + engagement_type: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + switch (engagement_type) { + case 'CALL': + return await this.unifyCall( + source as CloseEngagementCallOutput | CloseEngagementCallOutput[], + customFieldMappings, + ); + case 'MEETING': + return await this.unifyMeeting( + source as + | CloseEngagementMeetingOutput + | CloseEngagementMeetingOutput[], + customFieldMappings, + ); + case 'EMAIL': + return await this.unifyEmail( + source as CloseEngagementEmailOutput | CloseEngagementEmailOutput[], + customFieldMappings, + ); + default: + break; + } + } + + private async unifyCall( + source: CloseEngagementCallOutput | CloseEngagementCallOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementCallToUnified(source, customFieldMappings); + } + // Handling array of CloseEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementCallToUnified(engagement, customFieldMappings), + ), + ); + } + + private async unifyMeeting( + source: CloseEngagementMeetingOutput | CloseEngagementMeetingOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementMeetingToUnified( + source, + customFieldMappings, + ); + } + // Handling array of CloseEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementMeetingToUnified( + engagement, + customFieldMappings, + ), + ), + ); + } + + private async unifyEmail( + source: CloseEngagementEmailOutput | CloseEngagementEmailOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementEmailToUnified( + source, + customFieldMappings, + ); + } + // Handling array of CloseEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementEmailToUnified(engagement, customFieldMappings), + ), + ); + } + + private async mapSingleEngagementCallToUnified( + engagement: CloseEngagementCallOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + 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.created_by || engagement.user_id) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + engagement.created_by || 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.note_html || engagement.note, + subject: engagement.note, + start_at: new Date(engagement.date_created), + end_time: new Date(engagement.date_updated), // Assuming end time is mapped from last modified date + type: 'CALL', + direction: engagement.direction, + field_mappings, + ...opts, + }; + } + + private async mapSingleEngagementMeetingToUnified( + engagement: CloseEngagementMeetingOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + 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.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.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 { + 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, + }; + } +} diff --git a/packages/api/src/crm/engagement/services/close/types.ts b/packages/api/src/crm/engagement/services/close/types.ts new file mode 100644 index 000000000..97c7430c4 --- /dev/null +++ b/packages/api/src/crm/engagement/services/close/types.ts @@ -0,0 +1,221 @@ +interface Call { + lead_id: string; + contact_id: string; + created_by: string; + user_id: string; + direction: string; + status: string; + note_html: string; + duration: number; + phone: string; +} + +export type CloseEngagementCallInput = Partial; + +interface OutputCall { + id: string; + _type: string; + recording_url: string | null; + voicemail_url: string | null; + voicemail_duration: number | null; + direction: string; + disposition: string; + source: string; + note_html: string; + note: string; + duration: number; + local_phone: string; + local_phone_formatted: string; + remote_phone: string; + remote_phone_formatted: string; + phone: string; + created_by: string; + updated_by: string; + date_created: string; + date_updated: string; + organization_id: string; + user_id: string; + lead_id: string; + contact_id: string; + call_method: string; + dialer_id: string | null; + dialer_saved_search_id: string | null; + cost: string; + local_country_iso: string; + remote_country_iso: string; +} + +export type CloseEngagementCallOutput = Partial; + +export interface CloseEngagementMeetingInput { + note_html?: string; +} + +interface Meeting { + id: string; + _type: string; + title: string; + location: string; + status: string; + note: string; + starts_at: string; // Date in ISO 8601 format + ends_at: string; // Date in ISO 8601 format + duration: number; // Duration of the meeting in seconds + date_created: string; // Date in ISO 8601 format + date_updated: string; // Date in ISO 8601 format + created_by: string | null; + created_by_name: string | null; + updated_by: string | null; + updated_by_name: string | null; + user_id: string; + user_name: string; + organization_id: string; + connected_account_id: string; + source: string; + is_recurring: boolean; + lead_id: string | null; + contact_id: string | null; + attendees: Attendee[]; +} + +interface Attendee { + status: string; + user_id: string | null; + name: string | null; + contact_id: string | null; + is_organizer: boolean; + email: string; +} + +export type CloseEngagementMeetingOutput = Partial; + +export const commonMeetingCloseProperties = { + createdate: '', + hs_internal_meeting_notes: '', + hs_lastmodifieddate: '', + hs_meeting_body: '', + hs_meeting_end_time: '', + hs_meeting_external_url: '', + hs_meeting_location: '', + hs_meeting_outcome: '', + hs_meeting_start_time: '', + hs_meeting_title: '', + hs_timestamp: '', + close_owner_id: '', +}; + +interface Email { + contact_id: string; + user_id: string; + lead_id: string; + direction: string; + created_by: string | null; + created_by_name: string; + date_created: string; + subject: string; + sender: string; + to: string[]; + bcc: string[]; + cc: string[]; + status: string; + body_text: string; + body_html: string; + attachments: Attachment[]; + email_account_id: string; + template_id: string | null; +} + +interface Attachment { + url: string; + filename: string; + size: number; + content_type: string; +} + +export type CloseEngagementEmailInput = Partial; + +interface EmailOuput { + attachments: any[]; + body_text: string; + date_updated: string; + direction: string; + contact_id: string; + id: string; + user_id: string; + created_by: string | null; + to: string[]; + subject: string; + opens: any[]; + status: string; + _type: string; + updated_by: string; + updated_by_name: string; + envelope: Envelope; + body_html: string; + organization_id: string; + body_text_quoted: BodyTextQuoted[]; + send_attempts: any[]; + lead_id: string; + sender: string; + bcc: any[]; + date_created: string; + template_id: string | null; + cc: any[]; + sequence_subscription_id: string; + sequence_id: string; + sequence_name: string; +} + +interface Envelope { + from: EmailAddress[]; + sender: EmailAddress[]; + to: EmailAddress[]; + cc: any[]; + bcc: any[]; + reply_to: any[]; + date: string; + in_reply_to: any; + message_id: string; + subject: string; +} + +interface EmailAddress { + email: string; + name: string; +} + +interface BodyTextQuoted { + text: string; + expand: boolean; +} + +// Example usage + +export type CloseEngagementEmailOutput = Partial; + +export const commonEmailCloseProperties = { + createdate: '', + hs_email_direction: '', + hs_email_sender_email: '', + hs_email_sender_firstname: '', + hs_email_sender_lastname: '', + hs_email_status: '', + hs_email_subject: '', + hs_email_text: '', + hs_email_to_email: '', + hs_email_to_firstname: '', + hs_email_to_lastname: '', + hs_lastmodifieddate: '', + hs_timestamp: '', + close_owner_id: '', +}; + +export type CloseEngagementInput = + | CloseEngagementCallInput + | CloseEngagementMeetingInput + | CloseEngagementEmailInput; + +export type CloseEngagementOutput = + | CloseEngagementCallOutput + | CloseEngagementMeetingOutput + | CloseEngagementEmailOutput; diff --git a/packages/api/src/crm/engagement/sync/sync.service.ts b/packages/api/src/crm/engagement/sync/sync.service.ts index bc535c672..9b13f3b96 100644 --- a/packages/api/src/crm/engagement/sync/sync.service.ts +++ b/packages/api/src/crm/engagement/sync/sync.service.ts @@ -169,7 +169,9 @@ export class SyncService implements OnModuleInit { //unify the data according to the target obj wanted const unifiedObject = (await unify({ sourceObject, - targetType: CrmObject.engagement, + targetType: `engagement${ + engagement_type ? `_${engagement_type}` : '' + }` as CrmObject, providerName: integrationId, vertical: 'crm', customFieldMappings, diff --git a/packages/api/src/crm/engagement/types/mappingsTypes.ts b/packages/api/src/crm/engagement/types/mappingsTypes.ts index c59c8cf6f..e78f433a5 100644 --- a/packages/api/src/crm/engagement/types/mappingsTypes.ts +++ b/packages/api/src/crm/engagement/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotEngagementMapper } from '../services/hubspot/mappers'; import { PipedriveEngagementMapper } from '../services/pipedrive/mappers'; import { ZendeskEngagementMapper } from '../services/zendesk/mappers'; import { ZohoEngagementMapper } from '../services/zoho/mappers'; +import { CloseEngagementMapper } from '../services/close/mappers'; const hubspotEngagementMapper = new HubspotEngagementMapper(); const zendeskEngagementMapper = new ZendeskEngagementMapper(); const zohoEngagementMapper = new ZohoEngagementMapper(); const pipedriveEngagementMapper = new PipedriveEngagementMapper(); +const closeEngagmentMapper = new CloseEngagementMapper(); export const engagementUnificationMapping = { hubspot: { @@ -27,4 +29,8 @@ export const engagementUnificationMapping = { unify: zendeskEngagementMapper.unify.bind(zendeskEngagementMapper), desunify: zendeskEngagementMapper.desunify.bind(zendeskEngagementMapper), }, + close: { + unify: closeEngagmentMapper.unify.bind(closeEngagmentMapper), + desunify: closeEngagmentMapper.desunify.bind(closeEngagmentMapper), + }, }; diff --git a/packages/api/src/crm/note/note.module.ts b/packages/api/src/crm/note/note.module.ts index 1c2de8847..ba97f6b97 100644 --- a/packages/api/src/crm/note/note.module.ts +++ b/packages/api/src/crm/note/note.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/note/services/close/index.ts b/packages/api/src/crm/note/services/close/index.ts new file mode 100644 index 000000000..ab278defd --- /dev/null +++ b/packages/api/src/crm/note/services/close/index.ts @@ -0,0 +1,105 @@ +import { Injectable } from '@nestjs/common'; +import { INoteService } from '@crm/note/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { CloseNoteInput, CloseNoteOutput } from './types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class CloseService implements INoteService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.note.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addNote( + noteData: CloseNoteInput, + linkedUserId: string, + ): Promise> { + 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}/activity/note`, + JSON.stringify(noteData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close note created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.note, + ActionType.POST, + ); + } + } + + async syncNotes( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/activity/note`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close notes !`); + return { + data: resp?.data?.data, + message: 'Close notes retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.note, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/note/services/close/mappers.ts b/packages/api/src/crm/note/services/close/mappers.ts new file mode 100644 index 000000000..31c12256a --- /dev/null +++ b/packages/api/src/crm/note/services/close/mappers.ts @@ -0,0 +1,125 @@ +import { CloseNoteInput, CloseNoteOutput } from './types'; +import { + UnifiedNoteInput, + UnifiedNoteOutput, +} from '@crm/note/types/model.unified'; +import { INoteMapper } from '@crm/note/types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseNoteMapper implements INoteMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedNoteInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: CloseNoteInput = { + note_html: source.content, + }; + + 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; + } + + async unify( + source: CloseNoteOutput | CloseNoteOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleNoteToUnified(source, customFieldMappings); + } + + return Promise.all( + source.map((note) => + this.mapSingleNoteToUnified(note, customFieldMappings), + ), + ); + } + + private async mapSingleNoteToUnified( + note: CloseNoteOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = note[mapping.remote_id]; + } + } + + let opts: any = {}; + if (note.created_by || note.user_id) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + note.created_by || note.user_id, + 'close', + ); + if (owner_id) { + opts = { + user_id: owner_id, + }; + } + } + if (note.contact_id) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + note.contact_id, + 'close', + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id, + }; + } + } + if (note.lead_id) { + const lead_id = await this.utils.getCompanyUuidFromRemoteId( + note.lead_id, + 'close', + ); + if (lead_id) { + opts = { + ...opts, + company_id: lead_id, + }; + } + } + return { + remote_id: note.id, + content: note.note_html, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/note/services/close/types.ts b/packages/api/src/crm/note/services/close/types.ts new file mode 100644 index 000000000..4add97287 --- /dev/null +++ b/packages/api/src/crm/note/services/close/types.ts @@ -0,0 +1,42 @@ +interface NoteInput { + note_html: string; + lead_id: string; + attachments: Attachment[]; +} + +interface Attachment { + content_type: string; + filename: string; + url: string; +} + +export type CloseNoteInput = Partial; + +interface Note { + organization_id: string; + _type: 'Note'; + user_id: string; + user_name: string; + updated_by: string; + updated_by_name: string; + date_updated: string; + created_by: string; + created_by_name: string; + note_html: string; + note: string; + attachments: Attachment[]; + contact_id: string | null; + date_created: string; + id: string; + lead_id: string; +} + +interface Attachment { + content_type: string; + filename: string; + size: number; + url: string; + thumbnail_url: string; +} + +export type CloseNoteOutput = Partial; diff --git a/packages/api/src/crm/note/types/mappingsTypes.ts b/packages/api/src/crm/note/types/mappingsTypes.ts index dbed4456e..43b7f3446 100644 --- a/packages/api/src/crm/note/types/mappingsTypes.ts +++ b/packages/api/src/crm/note/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotNoteMapper } from '../services/hubspot/mappers'; import { PipedriveNoteMapper } from '../services/pipedrive/mappers'; import { ZendeskNoteMapper } from '../services/zendesk/mappers'; import { ZohoNoteMapper } from '../services/zoho/mappers'; +import { CloseNoteMapper } from '../services/close/mappers'; const hubspotNoteMapper = new HubspotNoteMapper(); const zendeskNoteMapper = new ZendeskNoteMapper(); const zohoNoteMapper = new ZohoNoteMapper(); const pipedriveNoteMapper = new PipedriveNoteMapper(); +const closeNoteMapper = new CloseNoteMapper(); export const noteUnificationMapping = { hubspot: { @@ -25,4 +27,8 @@ export const noteUnificationMapping = { unify: zendeskNoteMapper.unify.bind(zendeskNoteMapper), desunify: zendeskNoteMapper.desunify.bind(zendeskNoteMapper), }, + close: { + unify: closeNoteMapper.unify.bind(closeNoteMapper), + desunify: closeNoteMapper.desunify.bind(closeNoteMapper), + }, }; diff --git a/packages/api/src/crm/stage/services/close/index.ts b/packages/api/src/crm/stage/services/close/index.ts new file mode 100644 index 000000000..d7d8b4eeb --- /dev/null +++ b/packages/api/src/crm/stage/services/close/index.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { IStageService } from '@crm/stage/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { CloseStageOutput, commonStageCloseProperties } from './types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class CloseService implements IStageService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.stage.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + + async syncStages( + linkedUserId: string, + deal_id: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const res = await this.prisma.crm_deals.findUnique({ + where: { id_crm_deal: deal_id }, + }); + const baseURL = `${connection.account_url}/activity/status_change/opportunity/?opportunity_id=${res.remote_id}`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close stages !`); + return { + data: resp?.data?.data, + message: 'Close stages retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.stage, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/stage/services/close/mappers.ts b/packages/api/src/crm/stage/services/close/mappers.ts new file mode 100644 index 000000000..c26e06354 --- /dev/null +++ b/packages/api/src/crm/stage/services/close/mappers.ts @@ -0,0 +1,54 @@ +import { CloseStageOutput, CloseStageInput } from './types'; +import { + UnifiedStageInput, + UnifiedStageOutput, +} from '@crm/stage/types/model.unified'; +import { IStageMapper } from '@crm/stage/types'; + +export class CloseStageMapper implements IStageMapper { + desunify( + source: UnifiedStageInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): CloseStageInput { + return {}; + } + + unify( + source: CloseStageOutput | CloseStageOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedStageOutput | UnifiedStageOutput[] { + if (!Array.isArray(source)) { + return this.mapSingleStageToUnified(source, customFieldMappings); + } + // Handling array of CloseStageOutput + return source.map((stage) => + this.mapSingleStageToUnified(stage, customFieldMappings), + ); + } + + private mapSingleStageToUnified( + stage: CloseStageOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedStageOutput { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = stage[mapping.remote_id]; + } + } + return { + remote_id: stage.id, + stage_name: stage.new_status_label, + field_mappings, + }; + } +} diff --git a/packages/api/src/crm/stage/services/close/types.ts b/packages/api/src/crm/stage/services/close/types.ts new file mode 100644 index 000000000..84868e02d --- /dev/null +++ b/packages/api/src/crm/stage/services/close/types.ts @@ -0,0 +1,52 @@ +export interface CloseStageInput { + email?: string; + firstname?: string; + phone?: string; + lastname?: string; + city?: string; + country?: string; + zip?: string; + state?: string; + address?: string; + mobilephone?: string; + close_owner_id?: string; + associatedcompanyid?: string; + fax?: string; + jobtitle?: string; + [key: string]: any; +} + +interface OpportunityStatusChange { + organization_id: string; + _type: string; + contact_id: string | null; + created_by: string; + created_by_name: string; + date_created: string; + date_updated: string; + lead_id: string; + new_status_id: string; + new_status_label: string; + new_status_type: string; + new_pipeline_id: string; + old_status_id: string; + old_status_label: string; + old_status_type: string; + old_pipeline_id: string; + opportunity_date_won: string; + opportunity_id: string; + opportunity_value: number; + opportunity_value_formatted: string | null; + opportunity_value_currency: string; + updated_by: string; + updated_by_name: string; + user_id: string; + user_name: string; + id: string; +} + +export type CloseStageOutput = Partial; + +export const commonStageCloseProperties = { + dealstage: '', +}; diff --git a/packages/api/src/crm/stage/stage.module.ts b/packages/api/src/crm/stage/stage.module.ts index 3105933f1..32bd79860 100644 --- a/packages/api/src/crm/stage/stage.module.ts +++ b/packages/api/src/crm/stage/stage.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/stage/types/mappingsTypes.ts b/packages/api/src/crm/stage/types/mappingsTypes.ts index cf9ca7ef1..3b26a390f 100644 --- a/packages/api/src/crm/stage/types/mappingsTypes.ts +++ b/packages/api/src/crm/stage/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotStageMapper } from '../services/hubspot/mappers'; import { PipedriveStageMapper } from '../services/pipedrive/mappers'; import { ZendeskStageMapper } from '../services/zendesk/mappers'; import { ZohoStageMapper } from '../services/zoho/mappers'; +import { CloseStageMapper } from '../services/close/mappers'; const hubspotStageMapper = new HubspotStageMapper(); const zendeskStageMapper = new ZendeskStageMapper(); const zohoStageMapper = new ZohoStageMapper(); const pipedriveStageMapper = new PipedriveStageMapper(); +const closeStageMapper = new CloseStageMapper(); export const stageUnificationMapping = { hubspot: { @@ -25,4 +27,8 @@ export const stageUnificationMapping = { unify: zendeskStageMapper.unify.bind(zendeskStageMapper), desunify: zendeskStageMapper.desunify.bind(zendeskStageMapper), }, + close: { + unify: closeStageMapper.unify.bind(closeStageMapper), + desunify: closeStageMapper.desunify.bind(closeStageMapper), + }, }; diff --git a/packages/api/src/crm/task/services/close/index.ts b/packages/api/src/crm/task/services/close/index.ts new file mode 100644 index 000000000..49ca59e24 --- /dev/null +++ b/packages/api/src/crm/task/services/close/index.ts @@ -0,0 +1,105 @@ +import { Injectable } from '@nestjs/common'; +import { ITaskService } from '@crm/task/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { CloseTaskInput, CloseTaskOutput } from './types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class CloseService implements ITaskService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.task.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + async addTask( + taskData: CloseTaskInput, + linkedUserId: string, + ): Promise> { + 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}/task`, + JSON.stringify(taskData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Close task created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.task, + ActionType.POST, + ); + } + } + + async syncTasks( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/task`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced close tasks !`); + return { + data: resp?.data?.data, + message: 'Close tasks retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.task, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/task/services/close/mappers.ts b/packages/api/src/crm/task/services/close/mappers.ts new file mode 100644 index 000000000..61b9a9073 --- /dev/null +++ b/packages/api/src/crm/task/services/close/mappers.ts @@ -0,0 +1,140 @@ +import { CloseTaskInput, CloseTaskOutput } from './types'; +import { + UnifiedTaskInput, + UnifiedTaskOutput, +} from '@crm/task/types/model.unified'; +import { ITaskMapper } from '@crm/task/types'; +import { Utils } from '@crm/@lib/@utils'; + +export class CloseTaskMapper implements ITaskMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + async desunify( + source: UnifiedTaskInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + 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; + } + + async unify( + source: CloseTaskOutput | CloseTaskOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleTaskToUnified(source, customFieldMappings); + } + + return Promise.all( + source.map((task) => + this.mapSingleTaskToUnified(task, customFieldMappings), + ), + ); + } + + private async mapSingleTaskToUnified( + task: CloseTaskOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = task[mapping.remote_id]; + } + } + let opts: any = {}; + if (task.assigned_to) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + task.assigned_to, + 'close', + ); + if (owner_id) { + opts = { + user_id: owner_id, + }; + } + } + if (task.contact_id) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + task.contact_id, + 'close', + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id, + }; + } + } + if (task.lead_id) { + const lead_id = await this.utils.getCompanyUuidFromRemoteId( + task.lead_id, + 'close', + ); + if (lead_id) { + opts = { + ...opts, + company_id: lead_id, + }; + } + } + + return { + remote_id: task.id, + subject: '', + content: task.text, + status: task?.is_complete ? 'COMPLETED' : 'PENDING', + due_date: new Date(task.due_date), + finished_date: task.finished_date ? new Date(task.finished_date) : '', + field_mappings, + ...opts, + // Additional fields mapping based on UnifiedTaskOutput structure + }; + } +} diff --git a/packages/api/src/crm/task/services/close/types.ts b/packages/api/src/crm/task/services/close/types.ts new file mode 100644 index 000000000..19c56a518 --- /dev/null +++ b/packages/api/src/crm/task/services/close/types.ts @@ -0,0 +1,36 @@ +export interface CloseTaskInput { + _type: string; + lead_id: string; + assigned_to: string; + text: string; + date: string; + is_complete: boolean; +} + +interface LeadTask { + _type: string; + assigned_to: string; + assigned_to_name: string; + contact_id: string | null; + contact_name: string | null; + created_by: string; + created_by_name: string; + date: string; + date_created: string; + date_updated: string; + id: string; + is_complete: boolean; + is_dateless: boolean; + lead_id: string; + lead_name: string; + object_id: string | null; + object_type: string | null; + organization_id: string; + text: string; + updated_by: string; + updated_by_name: string; + due_date: string | null; + finished_date: string | null; +} + +export type CloseTaskOutput = Partial; diff --git a/packages/api/src/crm/task/task.module.ts b/packages/api/src/crm/task/task.module.ts index e392a5f94..486134a06 100644 --- a/packages/api/src/crm/task/task.module.ts +++ b/packages/api/src/crm/task/task.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/task/types/mappingsTypes.ts b/packages/api/src/crm/task/types/mappingsTypes.ts index 12c0fd786..37e4400ea 100644 --- a/packages/api/src/crm/task/types/mappingsTypes.ts +++ b/packages/api/src/crm/task/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotTaskMapper } from '../services/hubspot/mappers'; import { PipedriveTaskMapper } from '../services/pipedrive/mappers'; import { ZendeskTaskMapper } from '../services/zendesk/mappers'; import { ZohoTaskMapper } from '../services/zoho/mappers'; +import { CloseTaskMapper } from '../services/close/mappers'; const hubspotTaskMapper = new HubspotTaskMapper(); const zendeskTaskMapper = new ZendeskTaskMapper(); const zohoTaskMapper = new ZohoTaskMapper(); const pipedriveTaskMapper = new PipedriveTaskMapper(); +const closeTaskMapper = new CloseTaskMapper(); export const taskUnificationMapping = { hubspot: { @@ -25,4 +27,8 @@ export const taskUnificationMapping = { unify: zendeskTaskMapper.unify.bind(zendeskTaskMapper), desunify: zendeskTaskMapper.desunify.bind(zendeskTaskMapper), }, + close: { + unify: closeTaskMapper.unify.bind(closeTaskMapper), + desunify: closeTaskMapper.desunify.bind(closeTaskMapper), + }, }; diff --git a/packages/api/src/crm/user/services/close/index.ts b/packages/api/src/crm/user/services/close/index.ts new file mode 100644 index 000000000..d9ad8981b --- /dev/null +++ b/packages/api/src/crm/user/services/close/index.ts @@ -0,0 +1,67 @@ +import { Injectable } from '@nestjs/common'; +import { IUserService } from '@crm/user/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { CloseUserOutput } from './types'; +import axios from 'axios'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class CloseService implements IUserService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.user.toUpperCase() + ':' + CloseService.name, + ); + this.registry.registerService('close', this); + } + + async syncUsers( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'close', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/user`; + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + this.logger.log(`Synced close users !`); + + return { + data: resp?.data?.data, + message: 'Close users retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Close', + CrmObject.user, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/user/services/close/mappers.ts b/packages/api/src/crm/user/services/close/mappers.ts new file mode 100644 index 000000000..46ddf9fda --- /dev/null +++ b/packages/api/src/crm/user/services/close/mappers.ts @@ -0,0 +1,55 @@ +import { CloseUserInput, CloseUserOutput } from './types'; +import { + UnifiedUserInput, + UnifiedUserOutput, +} from '@crm/user/types/model.unified'; +import { IUserMapper } from '@crm/user/types'; + +export class CloseUserMapper implements IUserMapper { + desunify( + source: UnifiedUserInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): CloseUserInput { + return; + } + + unify( + source: CloseUserOutput | CloseUserOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedUserOutput | UnifiedUserOutput[] { + if (!Array.isArray(source)) { + return this.mapSingleUserToUnified(source, customFieldMappings); + } + // Handling array of CloseUserOutput + return source.map((user) => + this.mapSingleUserToUnified(user, customFieldMappings), + ); + } + + private mapSingleUserToUnified( + user: CloseUserOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedUserOutput { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = user[mapping.remote_id]; + } + } + return { + remote_id: user.id, + name: `${user.first_name} ${user.last_name}`, + email: user.email, + field_mappings, + }; + } +} diff --git a/packages/api/src/crm/user/services/close/types.ts b/packages/api/src/crm/user/services/close/types.ts new file mode 100644 index 000000000..b71512782 --- /dev/null +++ b/packages/api/src/crm/user/services/close/types.ts @@ -0,0 +1,14 @@ +export interface CloseUserInput { + [key: string]: any; +} + +export interface CloseUserOutput { + id: string; + email: string; + first_name: string; + last_name: string; + image: string; + organizations: string[]; + date_created: string; + date_updated: string; +} diff --git a/packages/api/src/crm/user/types/mappingsTypes.ts b/packages/api/src/crm/user/types/mappingsTypes.ts index df8a6ecbb..2c26f50f4 100644 --- a/packages/api/src/crm/user/types/mappingsTypes.ts +++ b/packages/api/src/crm/user/types/mappingsTypes.ts @@ -2,11 +2,13 @@ import { HubspotUserMapper } from '../services/hubspot/mappers'; import { PipedriveUserMapper } from '../services/pipedrive/mappers'; import { ZendeskUserMapper } from '../services/zendesk/mappers'; import { ZohoUserMapper } from '../services/zoho/mappers'; +import { CloseUserMapper } from '../services/close/mappers'; const hubspotUserMapper = new HubspotUserMapper(); const zendeskUserMapper = new ZendeskUserMapper(); const zohoUserMapper = new ZohoUserMapper(); const pipedriveUserMapper = new PipedriveUserMapper(); +const closeUserMapper = new CloseUserMapper(); export const userUnificationMapping = { hubspot: { @@ -25,4 +27,8 @@ export const userUnificationMapping = { unify: zendeskUserMapper.unify.bind(zendeskUserMapper), desunify: zendeskUserMapper.desunify.bind(zendeskUserMapper), }, + close: { + unify: closeUserMapper.unify.bind(closeUserMapper), + desunify: closeUserMapper.desunify.bind(closeUserMapper), + }, }; diff --git a/packages/api/src/crm/user/user.module.ts b/packages/api/src/crm/user/user.module.ts index 46960ac91..6d251c785 100644 --- a/packages/api/src/crm/user/user.module.ts +++ b/packages/api/src/crm/user/user.module.ts @@ -13,6 +13,7 @@ import { HubspotService } from './services/hubspot'; import { PipedriveService } from './services/pipedrive'; import { ZendeskService } from './services/zendesk'; import { ZohoService } from './services/zoho'; +import { CloseService } from './services/close'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { ZohoService } from './services/zoho'; ZohoService, PipedriveService, HubspotService, + CloseService, ], exports: [ SyncService, diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index 50d72e3cd..03177ba33 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -5077,7 +5077,7 @@ }, "country": { "type": "string", - "description": "The country." + "description": "The country" }, "address_type": { "type": "string", diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index bb2463234..98c1cf83d 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -95,7 +95,7 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { // construct the baseAuthUrl based on the fact that client may use custom subdomain const BASE_URL: string = providerName === 'gorgias' ? `${apiUrl}${baseUrl}` : - data.SUBDOMAIN ? data.SUBDOMAIN + baseUrl : baseUrl; + data.SUBDOMAIN ? data.SUBDOMAIN + baseUrl : baseUrl; // console.log('BASE URL IS '+ BASE_URL) if (!baseUrl || !BASE_URL) { @@ -106,7 +106,8 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { let params = `client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodedRedirectUrl}&state=${state}`; // Adding scope for providers that require it, except for 'pipedrive' - if (scopes) { + const ignoreScopes = ['close'] + if (scopes && !ignoreScopes.includes(providerName)) { params += `&scope=${encodeURIComponent(scopes)}`; } @@ -116,8 +117,9 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { params += '&response_type=code&access_type=offline'; break; case 'jira': + params = `audience=api.atlassian.com&${params}&prompt=consent&response_type=code`; case 'jira_service_mgmt': - params = `audience=api.atlassian.com&${params}&prompt=consent`; + params = `audience=api.atlassian.com&${params}&prompt=consen&response_type=codet`; break; case 'gitlab': params += '&response_type=code&code_challenge=&code_challenge_method='; diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index b1a2ffc88..7c3a770e9 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -3,7 +3,8 @@ export enum CrmConnectors { ZENDESK = 'zendesk', HUBSPOT = 'hubspot', PIPEDRIVE = 'pipedrive', - ATTIO = 'attio' + ATTIO = 'attio', + CLOSE = 'close' } export enum TicketingConnectors { diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index f7d873782..8de1dda5c 100644 --- a/packages/shared/src/connectors/index.ts +++ b/packages/shared/src/connectors/index.ts @@ -1,4 +1,4 @@ -export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio']; +export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close']; export const HRIS_PROVIDERS = ['']; export const ATS_PROVIDERS = ['']; export const ACCOUNTING_PROVIDERS = ['']; diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index a9ee5c056..ee0e4c55d 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -131,8 +131,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTEH77yPBUkStmoc1ZtgJS4XeBmQiaq_Q1vgF5oerOGbg&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: AuthStrategy.oauth2 + authStrategy: AuthStrategy.oauth2, }, 'copper': { scopes: '', @@ -335,7 +334,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { authStrategy: AuthStrategy.oauth2 }, 'jira': { - scopes: 'read:jira-work manage:jira-project manage:jira-data-provider manage:jira-webhook write:jira-work manage:jira-configuration read:jira-user offline_access', + scopes: 'read:jira-work manage:jira-project manage:jira-configuration read:jira-user write:jira-work manage:jira-webhook manage:jira-data-provider', urls: { docsUrl: '', apiUrl: '/rest/api/3', From 1eac23f11df4eed80dad56602d5e13240469074d Mon Sep 17 00:00:00 2001 From: nael Date: Wed, 12 Jun 2024 14:24:23 +0200 Subject: [PATCH 2/9] :bug: CURRENT FIX --- .../connections/connections.controller.ts | 38 +++++++++++++++++++ .../ticketing/services/jira/jira.service.ts | 30 ++++++++------- .../@core/encryption/encryption.service.ts | 2 +- packages/api/swagger/swagger-spec.json | 14 +++++++ packages/shared/src/connectors/metadata.ts | 2 +- 5 files changed, 71 insertions(+), 15 deletions(-) diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 5c16d7ca2..248e06492 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -5,6 +5,8 @@ import { Res, UseGuards, Request, + Post, + Body, } from '@nestjs/common'; import { Response } from 'express'; import { CrmConnectionsService } from './crm/services/crm.connection.service'; @@ -18,6 +20,8 @@ import { AccountingConnectionsService } from './accounting/services/accounting.c import { MarketingAutomationConnectionsService } from './marketingautomation/services/marketingautomation.connection.service'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; import { CoreSyncService } from '@@core/sync/sync.service'; +import { v4 as uuidv4 } from 'uuid'; +import { ConnectionUtils } from '@@core/connections/@utils'; export type StateDataType = { projectId: string; @@ -30,6 +34,8 @@ export type StateDataType = { @ApiTags('connections') @Controller('connections') export class ConnectionsController { + private readonly connectionUtils = new ConnectionUtils(); + constructor( private readonly crmConnectionsService: CrmConnectionsService, private readonly ticketingConnectionsService: TicketingConnectionsService, @@ -161,4 +167,36 @@ export class ConnectionsController { }, }); } + + @Post('create') + async createConnection(@Body() data: {access_token: string; refresh_token: string}) { + return await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: uuidv4(), + provider_slug: 'jira', + vertical: 'ticketing', + token_type: 'oauth', + account_url: `https://api.atlassian.com/ex/jira/9739da36-e4a6-42c8-b642-aab0c784d2ef`, + access_token: data.access_token, + refresh_token: data.refresh_token, + expiration_timestamp: new Date( + new Date().getTime() + Number(3600) * 1000, + ), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: "1e468c15-aa57-4448-aa2b-7fed640d1e3d" }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + "1e468c15-aa57-4448-aa2b-7fed640d1e3d", + "d44076f4-c1e6-4238-8940-a5e6fbc9f878", + ), + }, + }, + }, + }); + } } diff --git a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts index 6ab2f648c..be91d3a11 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts @@ -109,12 +109,14 @@ export class JiraConnectionService implements ITicketingConnectionService { const connection_token = uuidv4(); const access_token = this.cryptoService.encrypt(data.access_token); + const refresh_token = this.cryptoService.encrypt(data.refresh_token); + this.logger.log( - 'non-encrypted token is ----> ' + JSON.stringify(data.access_token), + 'encrypted token is ----> ' + JSON.stringify(access_token), ); this.logger.log( - 'encrypted token is ----> ' + JSON.stringify(access_token), + 'encrypted refresh token is ----> ' + JSON.stringify(refresh_token), ); if (isNotUnique) { @@ -123,8 +125,8 @@ export class JiraConnectionService implements ITicketingConnectionService { id_connection: isNotUnique.id_connection, }, data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), + access_token: access_token, + refresh_token: refresh_token, account_url: `https://api.atlassian.com/ex/jira/${cloud_id}`, expiration_timestamp: new Date( new Date().getTime() + Number(data.expires_in) * 1000, @@ -142,8 +144,8 @@ export class JiraConnectionService implements ITicketingConnectionService { vertical: 'ticketing', token_type: 'oauth', account_url: `https://api.atlassian.com/ex/jira/${cloud_id}`, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), + access_token: access_token, + refresh_token: refresh_token, expiration_timestamp: new Date( new Date().getTime() + Number(data.expires_in) * 1000, ), @@ -172,23 +174,25 @@ export class JiraConnectionService implements ITicketingConnectionService { async handleTokenRefresh(opts: RefreshParams) { try { const { connectionId, refreshToken, projectId } = opts; - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); + const CREDENTIALS = (await this.cService.getCredentials( projectId, this.type, )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + grant_type: 'refresh_token', + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + refresh_token: this.cryptoService.decrypt(refreshToken), + }); + const res = await axios.post( `https://auth.atlassian.com/oauth/token`, formData.toString(), { headers: { 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, }, }, ); diff --git a/packages/api/src/@core/encryption/encryption.service.ts b/packages/api/src/@core/encryption/encryption.service.ts index c2e4a2559..1707b7a2a 100644 --- a/packages/api/src/@core/encryption/encryption.service.ts +++ b/packages/api/src/@core/encryption/encryption.service.ts @@ -11,7 +11,7 @@ export class EncryptionService { this.secretKey = this.env.getCryptoKey(); } - encrypt(data: string) { + encrypt(data: string): string { try { const cipher = crypto.createCipheriv( 'aes-256-cbc', diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index 03177ba33..ccf6b07f1 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -365,6 +365,20 @@ ] } }, + "/connections/create": { + "post": { + "operationId": "ConnectionsController_createConnection", + "parameters": [], + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "connections" + ] + } + }, "/webhook": { "get": { "operationId": "getWebhooksMetadata", diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index ee0e4c55d..71a33f0b6 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -334,7 +334,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { authStrategy: AuthStrategy.oauth2 }, 'jira': { - scopes: 'read:jira-work manage:jira-project manage:jira-configuration read:jira-user write:jira-work manage:jira-webhook manage:jira-data-provider', + scopes: 'read:jira-work manage:jira-project manage:jira-configuration read:jira-user write:jira-work manage:jira-webhook manage:jira-data-provider offline_access', urls: { docsUrl: '', apiUrl: '/rest/api/3', From 2ee4040ff71616c5c24a938cc7e61492bc4093ce Mon Sep 17 00:00:00 2001 From: nael Date: Thu, 13 Jun 2024 14:36:17 +0200 Subject: [PATCH 3/9] :sparkles: New errors --- .../src/app/(Dashboard)/api-keys/page.tsx | 2 +- .../CustomLoginComponent/CreateUserForm.tsx | 2 +- .../CustomLoginComponent/LoginUserForm.tsx | 2 +- .../Configuration/Catalog/CatalogWidget.tsx | 2 +- .../Connector/ConnectorDisplay.tsx | 17 +- .../FieldMappings/Stepper/stepper-form.tsx | 8 +- .../FieldMappings/defineForm.tsx | 2 +- .../Configuration/FieldMappings/mapForm.tsx | 2 +- .../LinkedUsers/AddLinkedAccount.tsx | 4 +- .../Configuration/Webhooks/AddWebhook.tsx | 2 +- .../Configuration/Webhooks/columns.tsx | 6 +- .../Connection/AddConnectionButton.tsx | 2 +- .../shared/data-table-row-actions.tsx | 4 +- .../src/components/shared/team-switcher.tsx | 2 +- .../src/components/ui/file-uploader.tsx | 4 +- .../src/hooks/create/useCreateApiKey.tsx | 3 +- .../hooks/create/useCreateBatchLinkedUser.tsx | 3 +- .../create/useCreateConnectionStrategy.tsx | 3 +- .../src/hooks/create/useCreateLinkedUser.tsx | 3 +- .../src/hooks/create/useCreateMagicLink.tsx | 3 +- .../src/hooks/create/useCreateProfile.tsx | 3 +- .../src/hooks/create/useCreateProject.tsx | 3 +- .../src/hooks/create/useCreateUser.tsx | 3 +- .../src/hooks/create/useCreateWebhook.tsx | 3 +- .../src/hooks/create/useDefineField.tsx | 3 +- .../src/hooks/create/useMapField.tsx | 3 +- .../hooks/create/useRefreshAccessToken.tsx | 3 +- .../src/hooks/delete/useDeleteApiKey.tsx | 3 +- .../delete/useDeleteConnectionStrategy.tsx | 3 +- .../src/hooks/delete/useDeleteWebhook.tsx | 3 +- apps/client-ts/src/hooks/get/useApiKeys.tsx | 5 +- .../src/hooks/get/useConnectionStrategies.tsx | 7 +- .../useConnectionStrategyAuthCredentials.tsx | 9 +- .../src/hooks/get/useConnections.tsx | 5 +- apps/client-ts/src/hooks/get/useEvents.tsx | 3 +- .../src/hooks/get/useFieldMappings.tsx | 5 +- .../src/hooks/get/useLinkedUsers.tsx | 9 +- .../src/hooks/get/useProjectConnectors.tsx | 3 +- apps/client-ts/src/hooks/get/useProjects.tsx | 7 +- .../src/hooks/get/useProviderProperties.tsx | 7 +- apps/client-ts/src/hooks/get/useUser.tsx | 4 +- apps/client-ts/src/hooks/get/useWebhooks.tsx | 9 +- .../update/useUpdateConnectionStrategy.tsx | 3 +- .../update/useUpdateProjectConnectors.tsx | 3 +- .../hooks/update/useUpdateWebhookStatus.tsx | 3 +- packages/api/prisma/schema.prisma | 1 - packages/api/src/@core/auth/auth.service.ts | 174 +++++++++---- .../connections-strategies.service.ts | 244 +++++++++++------- .../services/accounting.connection.service.ts | 18 +- .../services/freeagent/freeagent.service.ts | 26 +- .../services/freshbooks/freshbooks.service.ts | 31 ++- .../services/moneybird/moneybird.service.ts | 26 +- .../services/pennylane/pennylane.service.ts | 26 +- .../services/quickbooks/quickbooks.service.ts | 31 ++- .../accounting/services/sage/sage.service.ts | 26 +- .../wave_financial/wave_financial.service.ts | 36 ++- .../connections/connections.controller.ts | 94 +++---- .../crm/services/accelo/accelo.service.ts | 26 +- .../crm/services/attio/attio.service.ts | 14 +- .../crm/services/capsule/capsule.service.ts | 26 +- .../crm/services/close/close.service.ts | 26 +- .../crm/services/copper/copper.service.ts | 14 +- .../crm/services/crm.connection.service.ts | 18 +- .../crm/services/hubspot/hubspot.service.ts | 26 +- .../crm/services/keap/keap.service.ts | 26 +- .../services/pipedrive/pipedrive.service.ts | 26 +- .../services/teamleader/teamleader.service.ts | 31 ++- .../crm/services/teamwork/teamwork.service.ts | 14 +- .../crm/services/zendesk/zendesk.service.ts | 26 +- .../crm/services/zoho/zoho.service.ts | 26 +- .../getresponse/getresponse.service.ts | 36 ++- .../services/mailchimp/mailchimp.service.ts | 14 +- .../marketingautomation.connection.service.ts | 18 +- .../services/podium/podium.service.ts | 26 +- .../ticketing/services/aha/aha.service.ts | 14 +- .../services/clickup/clickup.service.ts | 14 +- .../ticketing/services/front/front.service.ts | 26 +- .../services/github/github.service.ts | 26 +- .../services/gitlab/gitlab.service.ts | 26 +- .../services/gorgias/gorgias.service.ts | 26 +- .../ticketing/services/jira/jira.service.ts | 26 +- .../jira_service_mgmt/jira.service.ts | 36 ++- .../services/linear/linear.service.ts | 14 +- .../services/ticketing.connection.service.ts | 20 +- .../services/zendesk/zendesk.service.ts | 14 +- packages/api/src/@core/core.module.ts | 3 +- .../@core/encryption/encryption.service.ts | 20 +- .../api/src/@core/events/events.service.ts | 18 +- .../field-mapping/field-mapping.service.ts | 59 ++++- .../linked-users/linked-users.service.ts | 42 ++- .../@core/magic-link/magic-link.service.ts | 28 +- .../managed-webhooks.service.ts | 38 ++- .../project-connectors.service.ts | 39 ++- .../src/@core/projects/projects.service.ts | 32 ++- packages/api/src/@core/utils/errors.ts | 242 +++++++++++++++-- .../api/src/@core/webhook/webhook.service.ts | 74 +++++- .../src/ticketing/user/sync/sync.service.ts | 32 ++- packages/api/swagger/swagger-spec.json | 14 - 98 files changed, 1664 insertions(+), 533 deletions(-) diff --git a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx index dc0480787..bb71e4f9a 100644 --- a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx +++ b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx @@ -119,7 +119,7 @@ export default function Page() { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture('api_key_created', { diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx index 2b96b4427..4be8b7044 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx @@ -85,7 +85,7 @@ const CreateUserForm = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx index 4495f96fd..1e49ab5e0 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx @@ -81,7 +81,7 @@ const LoginUserForm = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx b/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx index 03e71fa52..28e2050ff 100644 --- a/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx +++ b/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx @@ -71,7 +71,7 @@ export function CatalogWidget() { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx index 3f39d905c..64714dab0 100644 --- a/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx +++ b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx @@ -130,7 +130,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_OAuth2_updated", { id_project: idProject, @@ -159,7 +159,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_OAuth2_created", { id_project: idProject, @@ -201,7 +201,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_API_KEY_updated", { id_project: idProject, @@ -230,7 +230,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_API_KEY_created", { id_project: idProject, @@ -277,7 +277,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_BASIC_AUTH_updated", { id_project: idProject, @@ -307,7 +307,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_BASIC_AUTH_created", { id_project: idProject, @@ -373,10 +373,9 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); - setSwitchEnabled(enabled); } }; diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx index 0a5eeef44..2ed589efc 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx @@ -147,8 +147,8 @@ function FirstStepForm({setClose}: {setClose: () => void}) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); posthog?.capture("field_defined", { id_project: idProject, mode: config.DISTRIBUTION @@ -323,8 +323,8 @@ function SecondStepForm({setClose}: {setClose: () => void}) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); nextStep(); posthog?.capture("field_mapped", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx index 89e0c2d66..9d20c2c91 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx @@ -103,7 +103,7 @@ export function DefineForm({ onClose }: {onClose: () => void}) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("field_defined", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx index 78befe50f..134aa013e 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx @@ -121,7 +121,7 @@ export function MapForm({ onClose, fieldToMap }: {onClose: () => void; fieldToMa ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("field_mapped", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx index 05ff1b49b..e03c12e71 100644 --- a/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx @@ -112,7 +112,7 @@ const [successImporting, setSuccessImporting]=useState(false) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); setShowNewLinkedUserDialog({open: false}) posthog?.capture("linked_account_created", { @@ -155,7 +155,7 @@ const [successImporting, setSuccessImporting]=useState(false) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("batch_linked_account_created", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx index 87b750a53..9323c7975 100644 --- a/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx +++ b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx @@ -101,7 +101,7 @@ const AddWebhook = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); handleOpenChange(false); posthog?.capture("webhook_created", { diff --git a/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx index 7be05a6b2..4fe4f4b44 100644 --- a/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx +++ b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx @@ -25,7 +25,7 @@ export function useColumns(webhooks: Webhook[] | undefined, setWebhooks: React.D id: webhook_id, active: status, }), - { + { loading: 'Loading...', success: (data: any) => { const index = webhooks!.findIndex(webhook => webhook.id_webhook_endpoint === webhook_id); @@ -44,8 +44,8 @@ export function useColumns(webhooks: Webhook[] | undefined, setWebhooks: React.D ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); } const handleCopy = (token: string) => { diff --git a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx index bd5af780d..28e8c65a9 100644 --- a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx +++ b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx @@ -126,7 +126,7 @@ const AddConnectionButton = ({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("magic_link_created", { diff --git a/apps/client-ts/src/components/shared/data-table-row-actions.tsx b/apps/client-ts/src/components/shared/data-table-row-actions.tsx index d81354487..00121144f 100644 --- a/apps/client-ts/src/components/shared/data-table-row-actions.tsx +++ b/apps/client-ts/src/components/shared/data-table-row-actions.tsx @@ -56,7 +56,7 @@ export function DataTableRowActions({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); break; case 'api-key': @@ -82,7 +82,7 @@ export function DataTableRowActions({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); break; default: diff --git a/apps/client-ts/src/components/shared/team-switcher.tsx b/apps/client-ts/src/components/shared/team-switcher.tsx index e1b3826e1..5e41ea909 100644 --- a/apps/client-ts/src/components/shared/team-switcher.tsx +++ b/apps/client-ts/src/components/shared/team-switcher.tsx @@ -129,7 +129,7 @@ export default function TeamSwitcher({ className ,projects}: TeamSwitcherProps) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); setShowNewDialog({open: false}) projectForm.reset(); diff --git a/apps/client-ts/src/components/ui/file-uploader.tsx b/apps/client-ts/src/components/ui/file-uploader.tsx index fe4f926a7..b7e2eb922 100644 --- a/apps/client-ts/src/components/ui/file-uploader.tsx +++ b/apps/client-ts/src/components/ui/file-uploader.tsx @@ -151,8 +151,8 @@ export function FileUploader(props: FileUploaderProps) { setFiles([]) return `${target} uploaded` }, - error: `Failed to upload ${target}`, - }) + error: (err: any) => err.message || 'Error' + }); } }, diff --git a/apps/client-ts/src/hooks/create/useCreateApiKey.tsx b/apps/client-ts/src/hooks/create/useCreateApiKey.tsx index 469926b6e..bdd0667ec 100644 --- a/apps/client-ts/src/hooks/create/useCreateApiKey.tsx +++ b/apps/client-ts/src/hooks/create/useCreateApiKey.tsx @@ -21,7 +21,8 @@ const useCreateApiKey = () => { }); if (!response.ok) { - throw new Error('Failed to add api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx b/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx index 169a8dfa2..383c03cab 100644 --- a/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx @@ -19,7 +19,8 @@ const useCreateBatchLinkedUser = () => { }); if (!response.ok) { - throw new Error('Failed to batch add linked user'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx b/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx index 72f7ab198..d727705e2 100644 --- a/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx @@ -20,7 +20,8 @@ const useCreateConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to add Connection Strategy'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx b/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx index 4d90f688b..e2cdfe666 100644 --- a/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx @@ -19,7 +19,8 @@ const useCreateLinkedUser = () => { }); if (!response.ok) { - throw new Error('Failed to add linked user'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx b/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx index 473e1263a..231e35bc5 100644 --- a/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx +++ b/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx @@ -21,7 +21,8 @@ const useCreateMagicLink = () => { }); if (!response.ok) { - throw new Error('Failed to generate link'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateProfile.tsx b/apps/client-ts/src/hooks/create/useCreateProfile.tsx index cc1f19bc7..549539db9 100644 --- a/apps/client-ts/src/hooks/create/useCreateProfile.tsx +++ b/apps/client-ts/src/hooks/create/useCreateProfile.tsx @@ -21,7 +21,8 @@ const useCreateProfile = () => { }); if (!response.ok) { - throw new Error('Failed to add profile'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateProject.tsx b/apps/client-ts/src/hooks/create/useCreateProject.tsx index 98e7e0468..c0050a0da 100644 --- a/apps/client-ts/src/hooks/create/useCreateProject.tsx +++ b/apps/client-ts/src/hooks/create/useCreateProject.tsx @@ -19,7 +19,8 @@ const useCreateProject = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateUser.tsx b/apps/client-ts/src/hooks/create/useCreateUser.tsx index a4ac462b2..b35201593 100644 --- a/apps/client-ts/src/hooks/create/useCreateUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateUser.tsx @@ -21,7 +21,8 @@ const useCreateUser = () => { }); if (!response.ok) { - throw new Error("Email already associated with other account!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateWebhook.tsx b/apps/client-ts/src/hooks/create/useCreateWebhook.tsx index 8bb644674..aa2169df3 100644 --- a/apps/client-ts/src/hooks/create/useCreateWebhook.tsx +++ b/apps/client-ts/src/hooks/create/useCreateWebhook.tsx @@ -20,7 +20,8 @@ const useCreateWebhook = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useDefineField.tsx b/apps/client-ts/src/hooks/create/useDefineField.tsx index 985a39c57..6b75ec468 100644 --- a/apps/client-ts/src/hooks/create/useDefineField.tsx +++ b/apps/client-ts/src/hooks/create/useDefineField.tsx @@ -21,7 +21,8 @@ const useDefineField = () => { }); if (!response.ok) { - throw new Error('Failed to define field mapping'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useMapField.tsx b/apps/client-ts/src/hooks/create/useMapField.tsx index 019d5a069..0227a66cb 100644 --- a/apps/client-ts/src/hooks/create/useMapField.tsx +++ b/apps/client-ts/src/hooks/create/useMapField.tsx @@ -21,7 +21,8 @@ const useMapField = () => { }); if (!response.ok) { - throw new Error('Failed to map field mapping'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx b/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx index bedea3d80..d5dbc6ce4 100644 --- a/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx +++ b/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx @@ -20,7 +20,8 @@ const useRefreshAccessToken = () => { }); if (!response.ok) { - throw new Error("Login Failed!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx b/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx index 94c8a2459..0784d734c 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx @@ -17,7 +17,8 @@ const useDeleteApiKey = () => { }); if (!response.ok) { - throw new Error('Failed to delete api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx b/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx index 64304e31a..9dec95087 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx @@ -17,7 +17,8 @@ const useDeleteConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to delete cs'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx b/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx index 984d07d2d..7796e2dc9 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx @@ -17,7 +17,8 @@ const useDeleteWebhook = () => { }); if (!response.ok) { - throw new Error('Failed to delete api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/get/useApiKeys.tsx b/apps/client-ts/src/hooks/get/useApiKeys.tsx index 6bd57e3af..cf211ff8e 100644 --- a/apps/client-ts/src/hooks/get/useApiKeys.tsx +++ b/apps/client-ts/src/hooks/get/useApiKeys.tsx @@ -16,8 +16,9 @@ const useApiKeys = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx b/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx index 8adca921f..b3c587258 100644 --- a/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx +++ b/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx @@ -15,9 +15,10 @@ const useConnectionStrategies = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx b/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx index 5c28ed68c..3e41e2fb5 100644 --- a/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx +++ b/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx @@ -18,10 +18,11 @@ const useConnectionStrategyAuthCredentials = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }) - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); }; return useMutation({ diff --git a/apps/client-ts/src/hooks/get/useConnections.tsx b/apps/client-ts/src/hooks/get/useConnections.tsx index 166936958..27b4948bc 100644 --- a/apps/client-ts/src/hooks/get/useConnections.tsx +++ b/apps/client-ts/src/hooks/get/useConnections.tsx @@ -16,8 +16,9 @@ const useConnections = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useEvents.tsx b/apps/client-ts/src/hooks/get/useEvents.tsx index 3f726d02b..27d647f84 100644 --- a/apps/client-ts/src/hooks/get/useEvents.tsx +++ b/apps/client-ts/src/hooks/get/useEvents.tsx @@ -20,7 +20,8 @@ const fetchEvents = async (params: PaginationParams): Promise => { }); if (!response.ok) { - throw new Error('Network response was not ok'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/get/useFieldMappings.tsx b/apps/client-ts/src/hooks/get/useFieldMappings.tsx index d6cb88f54..dcdc3e28a 100644 --- a/apps/client-ts/src/hooks/get/useFieldMappings.tsx +++ b/apps/client-ts/src/hooks/get/useFieldMappings.tsx @@ -16,8 +16,9 @@ const useFieldMappings = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); }}); diff --git a/apps/client-ts/src/hooks/get/useLinkedUsers.tsx b/apps/client-ts/src/hooks/get/useLinkedUsers.tsx index 484b9575b..54405212d 100644 --- a/apps/client-ts/src/hooks/get/useLinkedUsers.tsx +++ b/apps/client-ts/src/hooks/get/useLinkedUsers.tsx @@ -15,10 +15,11 @@ const useLinkedUsers = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); } }); }; diff --git a/apps/client-ts/src/hooks/get/useProjectConnectors.tsx b/apps/client-ts/src/hooks/get/useProjectConnectors.tsx index e0f330704..a80325efc 100644 --- a/apps/client-ts/src/hooks/get/useProjectConnectors.tsx +++ b/apps/client-ts/src/hooks/get/useProjectConnectors.tsx @@ -19,7 +19,8 @@ const useProjectConnectors = (id: string) => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); } diff --git a/apps/client-ts/src/hooks/get/useProjects.tsx b/apps/client-ts/src/hooks/get/useProjects.tsx index 1d65335be..7db671f01 100644 --- a/apps/client-ts/src/hooks/get/useProjects.tsx +++ b/apps/client-ts/src/hooks/get/useProjects.tsx @@ -16,9 +16,10 @@ const useProjects = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useProviderProperties.tsx b/apps/client-ts/src/hooks/get/useProviderProperties.tsx index 9ed5a5bd2..d6c718ec8 100644 --- a/apps/client-ts/src/hooks/get/useProviderProperties.tsx +++ b/apps/client-ts/src/hooks/get/useProviderProperties.tsx @@ -14,9 +14,10 @@ const useProviderProperties = (linkedUserId: string, providerId: string, vertica 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useUser.tsx b/apps/client-ts/src/hooks/get/useUser.tsx index 21ecf9f91..36252faf3 100644 --- a/apps/client-ts/src/hooks/get/useUser.tsx +++ b/apps/client-ts/src/hooks/get/useUser.tsx @@ -28,9 +28,9 @@ const useUser = () => { if (!response.ok) { Cookies.remove('access_token') - throw new Error("Fetch User Failed!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } - return response.json(); }; return useMutation({ diff --git a/apps/client-ts/src/hooks/get/useWebhooks.tsx b/apps/client-ts/src/hooks/get/useWebhooks.tsx index 482580a96..7d15bf01d 100644 --- a/apps/client-ts/src/hooks/get/useWebhooks.tsx +++ b/apps/client-ts/src/hooks/get/useWebhooks.tsx @@ -16,10 +16,11 @@ const useWebhooks = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); } }); }; diff --git a/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx b/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx index cd32638ba..5e0ef5290 100644 --- a/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx @@ -26,7 +26,8 @@ const useUpdateConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to toggle cs'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); } else { diff --git a/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx b/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx index 219884810..4a0837b56 100644 --- a/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx @@ -19,7 +19,8 @@ const useUpdateProjectConnectors = () => { }); if (!response.ok) { - throw new Error('Failed to update catalog option'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx b/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx index de57151db..2d08a028f 100644 --- a/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx @@ -18,7 +18,8 @@ const useUpdateWebhookStatus = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index c957f3681..82eef4c9f 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -109,7 +109,6 @@ model connections { linked_users linked_users @relation(fields: [id_linked_user], references: [id_linked_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_11") projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_9") - @@unique([access_token, refresh_token], map: "index_3") @@index([id_project], map: "fk_1") @@index([id_linked_user], map: "fk_connections_to_linkedusersid") } diff --git a/packages/api/src/@core/auth/auth.service.ts b/packages/api/src/@core/auth/auth.service.ts index bfb90b677..1ef8c5d60 100644 --- a/packages/api/src/@core/auth/auth.service.ts +++ b/packages/api/src/@core/auth/auth.service.ts @@ -1,7 +1,5 @@ import { - BadRequestException, Injectable, - UnauthorizedException, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { CreateUserDto } from './dto/create-user.dto'; @@ -10,7 +8,7 @@ import * as bcrypt from 'bcrypt'; import * as crypto from 'crypto'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { AuthError, throwTypedError } from '@@core/utils/errors'; import { LoginDto } from './dto/login.dto'; import { VerifyUserDto } from './dto/verify-user.dto'; import { ProjectsService } from '@@core/projects/projects.service'; @@ -30,9 +28,16 @@ export class AuthService { try { return await this.prisma.users.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "GET_USERS_ERROR", + message: "AuthService.getUsers() call failed", + cause: error + } + ), this.logger) } } + async verifyUser(verifyUser: VerifyUserDto) { try { const user = await this.prisma.users.findUnique({ @@ -42,22 +47,18 @@ export class AuthService { }); if (!user) { - throw new UnauthorizedException('user does not exist!'); + throw new ReferenceError('User undefined!'); } return verifyUser; - - // const projects = await this.prisma.projects.findMany({ - // where: { - // id_user: user.id_user, - // }, - // }); - // return { - // ...user, - // projects: projects, - // }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "VERIFY_USER_ERROR", + message: "AuthService.verifyUser() call failed", + cause: error + } + ), this.logger) } } @@ -69,7 +70,13 @@ export class AuthService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "GET_API_KEYS_ERROR", + message: "AuthService.getApiKeys() call failed", + cause: error + } + ), this.logger) } } @@ -80,12 +87,23 @@ export class AuthService { }); if (foundUser) { - throw new BadRequestException('Email is already exists!!'); + new AuthError( + { + name: "EMAIL_ALREADY_EXISTS_ERROR", + message: `Email already exists for user with email=${user.email}`, + } + ) } return await this.createUser(user); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "REGISTER_USER_ERROR", + message: "AuthService.register() call failed", + cause: error + } + ), this.logger) } } @@ -104,15 +122,19 @@ export class AuthService { created_at: new Date(), }, }); - const pro = await this.projectService.createProject({ + await this.projectService.createProject({ name: 'Project 1', id_user: user_.id_user, - }); - this.logger.log('proj is ' + JSON.stringify(pro)); + }); return user_; } catch (error) { - console.log(error); - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "CREATE_USER_ERROR", + message: "AuthService.createUser() call failed", + cause: error + } + ), this.logger) } } @@ -124,15 +146,18 @@ export class AuthService { }, }); + if (!foundUser) { + throw new ReferenceError('User undefined!'); + } + const project = await this.prisma.projects.findFirst({ where: { id_user: foundUser.id_user, }, }); - this.logger.log('Project found (login) is ' + JSON.stringify(project)); - if (!foundUser) { - throw new UnauthorizedException('user does not exist!'); + if (!project) { + throw new ReferenceError('Project undefined!'); } const isEq = await bcrypt.compare( @@ -140,7 +165,7 @@ export class AuthService { foundUser.password_hash, ); - if (!isEq) throw new UnauthorizedException('Invalid credentials.'); + if (!isEq) throw new ReferenceError('Bcrypt Invalid credentials, mismatch in password.'); const { ...userData } = foundUser; @@ -164,7 +189,13 @@ export class AuthService { }), // token used to generate api keys }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "LOGIN_USER_ERROR", + message: "AuthService.login() call failed", + cause: error + } + ), this.logger) } } @@ -189,12 +220,28 @@ export class AuthService { }), }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "REFRESH_ACCESS_TOKEN_ERROR", + message: "AuthService.refreshAccessToken() call failed", + cause: error + } + ), this.logger) } } hashApiKey(apiKey: string): string { - return crypto.createHash('sha256').update(apiKey).digest('hex'); + try{ + return crypto.createHash('sha256').update(apiKey).digest('hex'); + }catch(error){ + throwTypedError(new AuthError( + { + name: "HASH_API_KEY_ERROR", + message: "AuthService.hashApiKey() call failed", + cause: error + } + ), this.logger) + } } //must be called only if user is logged in @@ -214,7 +261,13 @@ export class AuthService { }), }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "GENERATE_API_KEY_ERROR", + message: "AuthService.generateApiKey() call failed", + cause: error + } + ), this.logger) } } @@ -228,16 +281,16 @@ export class AuthService { where: { id_project: projectId }, }); if (!foundProject) { - throw new UnauthorizedException( - 'project not found inside api key function generation', + throw new ReferenceError( + 'project undefined', ); } const foundUser = await this.prisma.users.findUnique({ where: { id_user: userId }, }); if (!foundUser) { - throw new UnauthorizedException( - 'user not found inside api key function generation', + throw new ReferenceError( + 'user undefined', ); } @@ -258,21 +311,37 @@ export class AuthService { }, }); if (!new_api_key) { - throw new UnauthorizedException('api keys issue to add to db'); + throw new ReferenceError('api key undefined'); } return { api_key: access_token, ...new_api_key }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "GENERATE_API_KEY_ERROR", + message: "AuthService.generateApiKeyForUser() call failed", + cause: error + } + ), this.logger) } } async deleteApiKey(apiKeyId: string) { - return await this.prisma.api_keys.delete({ - where: { - id_api_key: apiKeyId, - }, - }); + try{ + return await this.prisma.api_keys.delete({ + where: { + id_api_key: apiKeyId, + }, + }); + }catch(error){ + throwTypedError(new AuthError( + { + name: "DELETE_API_KEY_ERROR", + message: "AuthService.deleteApiKey() call failed", + cause: error + } + ), this.logger) + } } async validateApiKey(apiKey: string): Promise { @@ -288,24 +357,31 @@ export class AuthService { api_key_hash: apiKey, }, }); + if (!saved_api_key) { - throw new UnauthorizedException('Failed to fetch API key from DB'); + throw new ReferenceError('Api Key undefined'); } if (String(decoded.projectId) !== String(saved_api_key.id_project)) { - throw new UnauthorizedException( - 'Failed to validate API key: projectId invalid.', + throw new ReferenceError( + 'Failed to validate API key: projectId mismatch.', ); } // Validate that the JWT payload matches the provided userId and projectId if (String(decoded.sub) !== String(saved_api_key.id_user)) { - throw new UnauthorizedException( - 'Failed to validate API key: userId invalid.', + throw new ReferenceError( + 'Failed to validate API key: userId mismatch.', ); } return true; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new AuthError( + { + name: "VALIDATE_API_KEY_ERROR", + message: "AuthService.validateApiKey() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts index b221efdfb..3fe1c5803 100644 --- a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts +++ b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts @@ -1,5 +1,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; +import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; +import { ConnectionStrategiesError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { @@ -30,18 +32,30 @@ export class ConnectionsStrategiesService { private prisma: PrismaService, private crypto: EncryptionService, private configService: ConfigService, + private logger: LoggerService, ) {} async isCustomCredentials(projectId: string, type: string) { - const res = await this.prisma.connection_strategies.findFirst({ - where: { - id_project: projectId, - type: type, - status: true, - }, - }); - if (!res) return false; - return res.status; + try{ + const res = await this.prisma.connection_strategies.findFirst({ + where: { + id_project: projectId, + type: type, + status: true, + }, + }); + if (!res) throw ReferenceError('Connection strategy undefined') + return res.status; + }catch(error){ + throwTypedError(new ConnectionStrategiesError( + { + name: "CUSTOM_CREDENTIALS_ERROR", + message: "ConnectionsStrategiesService.isCustomCredentials() call failed", + cause: error + } + ), this.logger) + } + } async createConnectionStrategy( @@ -50,51 +64,64 @@ export class ConnectionsStrategiesService { attributes: string[], values: string[], ) { - const checkCSDuplicate = await this.prisma.connection_strategies.findFirst({ - where: { - id_project: projectId, - type: type, - }, - }); - if (checkCSDuplicate) - throw new Error('The Connection Strategy already exists!'); - - const cs = await this.prisma.connection_strategies.create({ - data: { - id_connection_strategy: uuidv4(), - id_project: projectId, - type: type, - status: true, - }, - }); - const entity = await this.prisma.cs_entities.create({ - data: { - id_cs_entity: uuidv4(), - id_connection_strategy: cs.id_connection_strategy, - }, - }); - for (let i = 0; i < attributes.length; i++) { - const attribute_slug = attributes[i]; - const value = values[i]; - //create all attributes (for oauth => client_id, client_secret) - const attribute_ = await this.prisma.cs_attributes.create({ + try{ + const checkCSDuplicate = await this.prisma.connection_strategies.findFirst({ + where: { + id_project: projectId, + type: type, + }, + }); + if (checkCSDuplicate) + throw new ConnectionStrategiesError({ + name: "CONNECTION_STRATEGY_ALREADY_EXISTS", + message: `Connection strategy already exists for projectId=${projectId} and type=${type}` + }); + + const cs = await this.prisma.connection_strategies.create({ data: { - id_cs_attribute: uuidv4(), - id_cs_entity: entity.id_cs_entity, - attribute_slug: attribute_slug, - data_type: 'string', + id_connection_strategy: uuidv4(), + id_project: projectId, + type: type, + status: true, }, }); - const value_ = await this.prisma.cs_values.create({ + const entity = await this.prisma.cs_entities.create({ data: { - id_cs_value: uuidv4(), - value: this.crypto.encrypt(value), - id_cs_attribute: attribute_.id_cs_attribute, + id_cs_entity: uuidv4(), + id_connection_strategy: cs.id_connection_strategy, }, }); + for (let i = 0; i < attributes.length; i++) { + const attribute_slug = attributes[i]; + const value = values[i]; + //create all attributes (for oauth => client_id, client_secret) + const attribute_ = await this.prisma.cs_attributes.create({ + data: { + id_cs_attribute: uuidv4(), + id_cs_entity: entity.id_cs_entity, + attribute_slug: attribute_slug, + data_type: 'string', + }, + }); + const value_ = await this.prisma.cs_values.create({ + data: { + id_cs_value: uuidv4(), + value: this.crypto.encrypt(value), + id_cs_attribute: attribute_.id_cs_attribute, + }, + }); + } + + return cs; + }catch(error){ + throwTypedError(new ConnectionStrategiesError( + { + name: "CREATE_CONNECTION_STRATEGY_ERROR", + message: "ConnectionsStrategiesService.createConnectionStrategy() call failed", + cause: error + } + ), this.logger) } - - return cs; } async toggle(id_cs: string) { @@ -104,7 +131,7 @@ export class ConnectionsStrategiesService { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); // Toggle the 'active' value const updatedCs = await this.prisma.connection_strategies.update({ where: { @@ -117,7 +144,13 @@ export class ConnectionsStrategiesService { return updatedCs; } catch (error) { - throw new Error(error); + throwTypedError(new ConnectionStrategiesError( + { + name: "TOGGLE_CONNECTION_STRATEGY_ERROR", + message: "ConnectionsStrategiesService.toggle() call failed", + cause: error + } + ), this.logger) } } @@ -134,12 +167,14 @@ export class ConnectionsStrategiesService { type: type, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); const entity = await this.prisma.cs_entities.findFirst({ where: { id_connection_strategy: cs.id_connection_strategy, }, }); + if (!entity) throw new ReferenceError('Connection strategy entity undefined !'); + const authValues: string[] = []; for (let i = 0; i < attributes.length; i++) { const attribute_slug = attributes[i]; @@ -150,13 +185,13 @@ export class ConnectionsStrategiesService { attribute_slug: attribute_slug, }, }); - if (!attribute_) throw new Error('No attribute found !'); + if (!attribute_) throw new ReferenceError('Connection Strategy Attribute undefined !'); const value_ = await this.prisma.cs_values.findFirst({ where: { id_cs_attribute: attribute_.id_cs_attribute, }, }); - if (!value_) throw new Error('No value found !'); + if (!value_) throw new ReferenceError('Connection Strategy Value undefined !'); authValues.push(this.crypto.decrypt(value_.value)); } return authValues; @@ -286,38 +321,48 @@ export class ConnectionsStrategiesService { } async getCredentials(projectId: string, type: string) { - const isCustomCred = await this.isCustomCredentials(projectId, type); - const provider = extractProvider(type); - const vertical = extractVertical(type); - //TODO: extract sofwtaremode - if (!vertical) - throw new Error(`vertical not found for provider ${provider}`); - const authStrategy = extractAuthMode(type); - if (!authStrategy) - throw new Error(`auth strategy not found for provider ${provider}`); - - if (isCustomCred) { - //customer is using custom credentials (set in the webapp UI) - //fetch the right credentials - return await this.getCustomCredentialsData( - projectId, - type, - provider, - vertical, - authStrategy, - ); - } else { - // type is of form = HUBSPOT_CRM_CLOUD_OAUTH so we must extract the parts - return this.getEnvData( - provider, - vertical, - authStrategy, - SoftwareMode.cloud, - ); + try{ + const isCustomCred = await this.isCustomCredentials(projectId, type); + const provider = extractProvider(type); + const vertical = extractVertical(type); + //TODO: extract sofwtaremode + if (!vertical) + throw new ReferenceError(`vertical not found for provider ${provider}`); + const authStrategy = extractAuthMode(type); + if (!authStrategy) + throw new ReferenceError(`auth strategy not found for provider ${provider}`); + + if (isCustomCred) { + //customer is using custom credentials (set in the webapp UI) + //fetch the right credentials + return await this.getCustomCredentialsData( + projectId, + type, + provider, + vertical, + authStrategy, + ); + } else { + // type is of form = HUBSPOT_CRM_CLOUD_OAUTH so we must extract the parts + return this.getEnvData( + provider, + vertical, + authStrategy, + SoftwareMode.cloud, + ); + } + }catch(error){ + throwTypedError(new ConnectionStrategiesError( + { + name: "GET_CREDENTIALS_ERROR", + message: "ConnectionsStrategiesService.getCredentials() call failed", + cause: error + } + ), this.logger) } + } - // Fetching all connection strategies for Project async getConnectionStrategiesForProject(projectId: string) { try { return await this.prisma.connection_strategies.findMany({ @@ -326,11 +371,16 @@ export class ConnectionsStrategiesService { }, }); } catch (error) { - throw new Error('Connection Strategies for projectID is not found!'); + throwTypedError(new ConnectionStrategiesError( + { + name: "GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR", + message: "ConnectionsStrategiesService.getConnectionStrategiesForProject() call failed", + cause: error + } + ), this.logger) } } - // update connection strategy async updateConnectionStrategy( id_cs: string, status: boolean, @@ -338,13 +388,13 @@ export class ConnectionsStrategiesService { values: string[], ) { try { - console.log('In updateAPI xzx'); const cs = await this.prisma.connection_strategies.findFirst({ where: { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); + const updateCS = await this.prisma.connection_strategies.update({ where: { id_connection_strategy: id_cs, @@ -360,6 +410,8 @@ export class ConnectionsStrategiesService { }, }); + if (!id_cs_entity) throw new ReferenceError('Connection strategy entity undefined !'); + for (let i = 0; i < attributes.length; i++) { const attribute_slug = attributes[i]; const value = values[i]; @@ -383,13 +435,16 @@ export class ConnectionsStrategiesService { } return cs; } catch (error) { - console.log('Error xzx'); - console.log(error); - throw new Error('Update Failed'); + throwTypedError(new ConnectionStrategiesError( + { + name: "UPDATE_CONNECTION_STRATEGY_ERROR", + message: "ConnectionsStrategiesService.updateConnectionStrategy() call failed", + cause: error + } + ), this.logger) } } - // Delete connection strategy async deleteConnectionStrategy(id_cs: string) { try { const cs = await this.prisma.connection_strategies.findFirst({ @@ -397,13 +452,14 @@ export class ConnectionsStrategiesService { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); const { id_cs_entity } = await this.prisma.cs_entities.findFirst({ where: { id_connection_strategy: id_cs, }, }); + if (!id_cs_entity) throw new ReferenceError('Connection strategy entity undefined !'); const attributes = await this.prisma.cs_attributes.findMany({ where: { @@ -444,7 +500,13 @@ export class ConnectionsStrategiesService { return deleteCS; } catch (error) { - throw new Error('Update Failed'); + throwTypedError(new ConnectionStrategiesError( + { + name: "DELETE_CONNECTION_STRATEGY_ERROR", + message: "ConnectionsStrategiesService.deleteConnectionStrategy() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts index 82fb92811..b2b7b1d57 100644 --- a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts +++ b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -73,7 +73,13 @@ export class AccountingConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: "AccountingConnectionsService.handleAccountngCallback() call failed", + cause: error + } + ), this.logger) } } @@ -98,7 +104,13 @@ export class AccountingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: "AccountingConnectionsService.handleAccountingTokensRefresh() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts b/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts index 62357bd51..c2a5caf06 100644 --- a/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts +++ b/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,7 +138,17 @@ export class FreeagentConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'freeagent', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `FreeagentConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "freeagent", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -181,7 +191,17 @@ export class FreeagentConnectionService }); this.logger.log('OAuth credentials updated : freeagent '); } catch (error) { - handleServiceError(error, this.logger, 'freeagent', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `FreeagentConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "freeagent", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts b/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts index b07e6ab46..00349c6ae 100644 --- a/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts +++ b/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,12 +137,17 @@ export class FreshbooksConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'freshbooks', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `FreshbooksConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "freshbooks", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -186,7 +191,17 @@ export class FreshbooksConnectionService }); this.logger.log('OAuth credentials updated : freshbooks '); } catch (error) { - handleServiceError(error, this.logger, 'freshbooks', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `FreshbooksConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "freshbooks", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts b/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts index 44048e4fd..8c01b991d 100644 --- a/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts +++ b/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,7 +138,17 @@ export class MoneybirdConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'moneybird', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `MoneybirdConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "moneybird", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -179,7 +189,17 @@ export class MoneybirdConnectionService }); this.logger.log('OAuth credentials updated : moneybird '); } catch (error) { - handleServiceError(error, this.logger, 'moneybird', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `MoneybirdConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "moneybird", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts index 91d22333a..6c4b321e1 100644 --- a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts +++ b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, handleServiceError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,7 +136,17 @@ export class PennylaneConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'pennylane', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `PennylaneConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "pennylane", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -179,7 +189,17 @@ export class PennylaneConnectionService }); this.logger.log('OAuth credentials updated : pennylane '); } catch (error) { - handleServiceError(error, this.logger, 'pennylane', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `PennylaneConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "pennylane", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts b/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts index cd4647093..c1b683838 100644 --- a/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts +++ b/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,12 +138,17 @@ export class QuickbooksConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'quickbooks', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `QuickbooksConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "quickbooks", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -182,7 +187,17 @@ export class QuickbooksConnectionService }); this.logger.log('OAuth credentials updated : quickbooks '); } catch (error) { - handleServiceError(error, this.logger, 'quickbooks', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `QuickbooksConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "quickbooks", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts b/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts index 6fac99ed5..7b0543276 100644 --- a/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts +++ b/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -135,7 +135,17 @@ export class SageConnectionService implements IAccountingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'sage', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `SageConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "sage", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class SageConnectionService implements IAccountingConnectionService { }); this.logger.log('OAuth credentials updated : sage '); } catch (error) { - handleServiceError(error, this.logger, 'sage', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `SageConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "sage", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts b/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts index 7e695e705..3c7146481 100644 --- a/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts +++ b/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -143,12 +143,17 @@ export class WaveFinancialConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'wave_financial', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `WaveFinancialConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "wave_financial", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -194,12 +199,17 @@ export class WaveFinancialConnectionService }); this.logger.log('OAuth credentials updated : wave_financial '); } catch (error) { - handleServiceError( - error, - this.logger, - 'wave_financial', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `WaveFinancialConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "wave_financial", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 248e06492..23706f107 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -5,13 +5,11 @@ import { Res, UseGuards, Request, - Post, - Body, } from '@nestjs/common'; import { Response } from 'express'; import { CrmConnectionsService } from './crm/services/crm.connection.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; import { PrismaService } from '@@core/prisma/prisma.service'; import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; import { TicketingConnectionsService } from './ticketing/services/ticketing.connection.service'; @@ -20,8 +18,6 @@ import { AccountingConnectionsService } from './accounting/services/accounting.c import { MarketingAutomationConnectionsService } from './marketingautomation/services/marketingautomation.connection.service'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; import { CoreSyncService } from '@@core/sync/sync.service'; -import { v4 as uuidv4 } from 'uuid'; -import { ConnectionUtils } from '@@core/connections/@utils'; export type StateDataType = { projectId: string; @@ -34,16 +30,14 @@ export type StateDataType = { @ApiTags('connections') @Controller('connections') export class ConnectionsController { - private readonly connectionUtils = new ConnectionUtils(); - constructor( private readonly crmConnectionsService: CrmConnectionsService, private readonly ticketingConnectionsService: TicketingConnectionsService, private readonly accountingConnectionsService: AccountingConnectionsService, private readonly marketingAutomationConnectionsService: MarketingAutomationConnectionsService, - private readonly coreSyncService: CoreSyncService, private logger: LoggerService, private prisma: PrismaService, + private coreSync: CoreSyncService ) { this.logger.setContext(ConnectionsController.name); } @@ -65,13 +59,19 @@ export class ConnectionsController { ) { try { if (!state) - throw new NotFoundError( - `No Callback Params found for state, found ${state}`, - ); + new ConnectionsError( + { + name: "OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR", + message: `No Callback Params found for state, found ${state}`, + } + ) if (!code) - throw new NotFoundError( - `No Callback Params found for code, found ${code}`, - ); + new ConnectionsError( + { + name: "OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR", + message: `No Callback Params found for code, found ${code}`, + } + ) const stateData: StateDataType = JSON.parse(decodeURIComponent(state)); const { projectId, vertical, linkedUserId, providerName, returnUrl } = @@ -119,15 +119,21 @@ export class ConnectionsController { break; } // Performing Core Sync Service - /*this.coreSyncService.initialSync( + this.coreSync.initialSync( vertical.toLowerCase(), providerName, linkedUserId, projectId, - );*/ + ); res.redirect(returnUrl); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "OAUTH_CALLBACK_ERROR", + message: "ConnectionsController.handleCallback() call failed", + cause: error + } + ), this.logger) } } @@ -159,44 +165,22 @@ export class ConnectionsController { @UseGuards(JwtAuthGuard) @Get() async getConnections(@Request() req: any) { - const { id_project } = req.user; - console.log('Req data is:', req.user); - return await this.prisma.connections.findMany({ - where: { - id_project: id_project, - }, - }); - } - - @Post('create') - async createConnection(@Body() data: {access_token: string; refresh_token: string}) { - return await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: uuidv4(), - provider_slug: 'jira', - vertical: 'ticketing', - token_type: 'oauth', - account_url: `https://api.atlassian.com/ex/jira/9739da36-e4a6-42c8-b642-aab0c784d2ef`, - access_token: data.access_token, - refresh_token: data.refresh_token, - expiration_timestamp: new Date( - new Date().getTime() + Number(3600) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: "1e468c15-aa57-4448-aa2b-7fed640d1e3d" }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - "1e468c15-aa57-4448-aa2b-7fed640d1e3d", - "d44076f4-c1e6-4238-8940-a5e6fbc9f878", - ), - }, + try{ + const { id_project } = req.user; + console.log('Req data is:', req.user); + return await this.prisma.connections.findMany({ + where: { + id_project: id_project, }, - }, - }); + }); + }catch(error){ + throwTypedError(new ConnectionsError( + { + name: "GET_CONNECTIONS_ERROR", + message: "ConnectionsController.getConnections() call failed", + cause: error + } + ), this.logger) + } } } diff --git a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts index 223825385..255a66e00 100644 --- a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts +++ b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts @@ -7,7 +7,7 @@ import { import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from '../registry.service'; @@ -144,7 +144,17 @@ export class AcceloConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'accelo', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `AcceloConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "accelo", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -189,7 +199,17 @@ export class AcceloConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : accelo '); } catch (error) { - handleServiceError(error, this.logger, 'accelo', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `AcceloConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "accelo", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/attio/attio.service.ts b/packages/api/src/@core/connections/crm/services/attio/attio.service.ts index 65523aaae..ad10f5262 100644 --- a/packages/api/src/@core/connections/crm/services/attio/attio.service.ts +++ b/packages/api/src/@core/connections/crm/services/attio/attio.service.ts @@ -7,7 +7,7 @@ import { import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from '../registry.service'; @@ -131,7 +131,17 @@ export class AttioConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'attio', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `AttioConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "attio", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts b/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts index 383bea9b3..b17e72b71 100644 --- a/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts +++ b/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,7 +136,17 @@ export class CapsuleConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'capsule', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CapsuleConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "capsule", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class CapsuleConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : capsule '); } catch (error) { - handleServiceError(error, this.logger, 'capsule', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `CapsuleConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "capsule", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/close/close.service.ts b/packages/api/src/@core/connections/crm/services/close/close.service.ts index c71e2ebb5..02b5d1cf8 100644 --- a/packages/api/src/@core/connections/crm/services/close/close.service.ts +++ b/packages/api/src/@core/connections/crm/services/close/close.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,7 +137,17 @@ export class CloseConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'close', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CloseConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "close", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -182,7 +192,17 @@ export class CloseConnectionService implements ICrmConnectionService { } this.logger.log('OAuth credentials updated : close '); } catch (error) { - handleServiceError(error, this.logger, 'close', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `CloseConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "close", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/copper/copper.service.ts b/packages/api/src/@core/connections/crm/services/copper/copper.service.ts index 88b6d39d0..745b25707 100644 --- a/packages/api/src/@core/connections/crm/services/copper/copper.service.ts +++ b/packages/api/src/@core/connections/crm/services/copper/copper.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -126,7 +126,17 @@ export class CopperConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'copper', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CopperConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "copper", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts index f39da7a04..0add79829 100644 --- a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts +++ b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -81,7 +81,13 @@ export class CrmConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: "CrmConnectionsService.handleCRMCallBack() call failed", + cause: error + } + ), this.logger) } } @@ -106,7 +112,13 @@ export class CrmConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: "CrmConnectionsService.handleCRMTokensRefresh() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts b/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts index 0d83c3705..2bdc0ba37 100644 --- a/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts +++ b/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts @@ -7,7 +7,7 @@ import { RefreshParams, } from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; @@ -135,7 +135,17 @@ export class HubspotConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'hubspot', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `HubspotConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "hubspot", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -180,7 +190,17 @@ export class HubspotConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : hubspot '); } catch (error) { - handleServiceError(error, this.logger, 'hubspot', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `HubspotConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "hubspot", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/keap/keap.service.ts b/packages/api/src/@core/connections/crm/services/keap/keap.service.ts index e9ccd25ef..dc15c185c 100644 --- a/packages/api/src/@core/connections/crm/services/keap/keap.service.ts +++ b/packages/api/src/@core/connections/crm/services/keap/keap.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class KeapConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'keap', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `KeapConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "keap", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -177,7 +187,17 @@ export class KeapConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : keap '); } catch (error) { - handleServiceError(error, this.logger, 'keap', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `KeapConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "keap", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts b/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts index 9c90ffdd1..ffb67f281 100644 --- a/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts +++ b/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts @@ -6,7 +6,7 @@ import { ICrmConnectionService, RefreshParams, } from '../../types'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class PipedriveConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'pipedrive', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `PipedriveConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "pipedrive", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -179,7 +189,17 @@ export class PipedriveConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : pipedrive '); } catch (error) { - handleServiceError(error, this.logger, 'pipedrive', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `PipedriveConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "pipedrive", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts b/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts index 34286d993..4b40d1893 100644 --- a/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts +++ b/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,12 +136,17 @@ export class TeamleaderConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'teamleader', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `TeamleaderConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "teamleader", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -183,7 +188,17 @@ export class TeamleaderConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : teamleader '); } catch (error) { - handleServiceError(error, this.logger, 'teamleader', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `TeamleaderConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "teamleader", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts b/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts index 551f0bb50..7b8662a68 100644 --- a/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts +++ b/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -127,7 +127,17 @@ export class TeamworkConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'teamwork', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `TeamworkConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "teamwork", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts b/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts index e5c1729fa..e5b16709e 100644 --- a/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts +++ b/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts @@ -6,7 +6,7 @@ import { ICrmConnectionService, RefreshParams, } from '../../types'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -140,7 +140,17 @@ export class ZendeskConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `ZendeskConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } async handleTokenRefresh(opts: RefreshParams) { @@ -182,7 +192,17 @@ export class ZendeskConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : zendesk '); } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `ZendeskConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts index 231663d89..b3d30c43f 100644 --- a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts +++ b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts @@ -7,7 +7,7 @@ import { RefreshParams, } from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; -import { Action, NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, NotFoundError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; @@ -176,7 +176,17 @@ export class ZohoConnectionService implements ICrmConnectionService { return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zoho', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `ZohoConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "zoho", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } async handleTokenRefresh(opts: RefreshParams) { @@ -218,7 +228,17 @@ export class ZohoConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : zoho '); } catch (error) { - handleServiceError(error, this.logger, 'zoho', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `ZohoConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "zoho", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts b/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts index 6a94cd504..fa476b2b1 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -143,12 +143,17 @@ export class GetresponseConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'getresponse', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `GetresponseConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "getresponse", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -190,12 +195,17 @@ export class GetresponseConnectionService }); this.logger.log('OAuth credentials updated : getresponse '); } catch (error) { - handleServiceError( - error, - this.logger, - 'getresponse', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", + message: `GetresponseConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "getresponse", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts b/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts index 5b4657df3..3acbfee8d 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -149,7 +149,17 @@ export class MailchimpConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'mailchimp', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `MailchimpConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "mailchimp", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts index 03b9cf934..d32ebb0c4 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -73,7 +73,13 @@ export class MarketingAutomationConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: "MarketingAutomationConnectionsService.handleMarketingAutomationCallBack() call failed", + cause: error + } + ), this.logger) } } @@ -98,7 +104,13 @@ export class MarketingAutomationConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", + message: "MarketingAutomationConnectionsService.handleMarketingAutomationTokensRefresh() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts b/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts index b835f0059..cbf43c92d 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,7 +137,17 @@ export class PodiumConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'podium', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `PodiumConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "podium", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class PodiumConnectionService }); this.logger.log('OAuth credentials updated : podium '); } catch (error) { - handleServiceError(error, this.logger, 'podium', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", + message: `PodiumConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "podium", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts b/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts index c57090984..8cd846197 100644 --- a/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -127,7 +127,17 @@ export class AhaConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'aha', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `AhaConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "aha", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts b/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts index c08aa68ff..ef2cbcdd6 100644 --- a/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -121,7 +121,17 @@ export class ClickupConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'clickup', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `ClickupConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "clickup", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/front/front.service.ts b/packages/api/src/@core/connections/ticketing/services/front/front.service.ts index ecf33b70f..a4d5c0a94 100644 --- a/packages/api/src/@core/connections/ticketing/services/front/front.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/front/front.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -131,7 +131,17 @@ export class FrontConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'front', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `FrontConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "front", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -173,7 +183,17 @@ export class FrontConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : front '); } catch (error) { - handleServiceError(error, this.logger, 'front', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `FrontConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "front", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/github/github.service.ts b/packages/api/src/@core/connections/ticketing/services/github/github.service.ts index 1d8a6c3fc..388f559b1 100644 --- a/packages/api/src/@core/connections/ticketing/services/github/github.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/github/github.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -132,7 +132,17 @@ export class GithubConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'github', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GithubConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "github", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -175,7 +185,17 @@ export class GithubConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : github '); } catch (error) { - handleServiceError(error, this.logger, 'github', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GithubConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "github", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts b/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts index 2a30ebdbf..1c53d4302 100644 --- a/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class GitlabConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'gitlab', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GitlabConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "gitlab", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -177,7 +187,17 @@ export class GitlabConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : gitlab '); } catch (error) { - handleServiceError(error, this.logger, 'gitlab', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GitlabConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "gitlab", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts b/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts index 48c9a47e4..8c55de22b 100644 --- a/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -142,7 +142,17 @@ export class GorgiasConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'gorgias', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GorgiasConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "gorgias", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -185,7 +195,17 @@ export class GorgiasConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : gorgias '); } catch (error) { - handleServiceError(error, this.logger, 'gorgias', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GorgiasConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "gorgias", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts index be91d3a11..999ae0676 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -167,7 +167,17 @@ export class JiraConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'jira', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `JiraConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "jira", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -211,7 +221,17 @@ export class JiraConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : jira '); } catch (error) { - handleServiceError(error, this.logger, 'jira', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `JiraConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "jira", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts index fef87be92..d85c3b263 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -172,12 +172,17 @@ export class JiraServiceMgmtConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'jira_service_mgmt', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `JiraServiceMgmtConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "jira_service_mgmt", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -219,12 +224,17 @@ export class JiraServiceMgmtConnectionService }); this.logger.log('OAuth credentials updated : jira_service_mgmt '); } catch (error) { - handleServiceError( - error, - this.logger, - 'jira_service_mgmt', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `JiraServiceMgmtConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "linear", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts b/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts index 4674a04d2..84d5e6567 100644 --- a/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -130,7 +130,17 @@ export class LinearConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'linear', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `LinearConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "linear", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts index 519b41849..5c39cbc21 100644 --- a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -75,9 +75,15 @@ export class TicketingConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: "TicketingConnectionsService.handleTicketingCallBack() call failed", + cause: error + } + ), this.logger) } - } + } async handleTicketingTokensRefresh( connectionId: string, @@ -100,7 +106,13 @@ export class TicketingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: "TicketingConnectionsService.handleTicketingTokensRefresh() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts b/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts index 772abf7a0..1c4affc13 100644 --- a/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -172,7 +172,17 @@ export class ZendeskConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `ZendeskConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/core.module.ts b/packages/api/src/@core/core.module.ts index a06fc0f6d..918672f19 100644 --- a/packages/api/src/@core/core.module.ts +++ b/packages/api/src/@core/core.module.ts @@ -14,6 +14,7 @@ import { EncryptionService } from './encryption/encryption.service'; import { ConnectionsStrategiesModule } from './connections-strategies/connections-strategies.module'; import { SyncModule } from './sync/sync.module'; import { ProjectConnectorsModule } from './project-connectors/project-connectors.module'; +import { LoggerService } from './logger/logger.service'; @Module({ imports: [ @@ -48,6 +49,6 @@ import { ProjectConnectorsModule } from './project-connectors/project-connectors SyncModule, ProjectConnectorsModule, ], - providers: [EncryptionService], + providers: [EncryptionService, LoggerService], }) export class CoreModule {} diff --git a/packages/api/src/@core/encryption/encryption.service.ts b/packages/api/src/@core/encryption/encryption.service.ts index 1707b7a2a..efbc5c4d6 100644 --- a/packages/api/src/@core/encryption/encryption.service.ts +++ b/packages/api/src/@core/encryption/encryption.service.ts @@ -1,4 +1,6 @@ import { EnvironmentService } from '@@core/environment/environment.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { EncryptionError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; import * as crypto from 'crypto'; @@ -7,7 +9,7 @@ export class EncryptionService { private secretKey: string; private iv = crypto.randomBytes(16); - constructor(private env: EnvironmentService) { + constructor(private env: EnvironmentService, private logger: LoggerService) { this.secretKey = this.env.getCryptoKey(); } @@ -23,7 +25,13 @@ export class EncryptionService { const encryptedWithIv = this.iv.toString('hex') + ':' + encrypted; return encryptedWithIv; } catch (error) { - throw new Error('Encrypting error... ' + error); + throwTypedError(new EncryptionError( + { + name: "ENCRYPT_ERROR", + message: "EncryptionService.encrypt() call failed", + cause: error + } + ), this.logger) } } @@ -41,7 +49,13 @@ export class EncryptionService { decrypted += decipher.final('utf8'); return decrypted; } catch (error) { - throw new Error('Decrypting error... ' + error); + throwTypedError(new EncryptionError( + { + name: "DECRYPT_ERROR", + message: "EncryptionService.decrypt() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/events/events.service.ts b/packages/api/src/@core/events/events.service.ts index bab3e5126..7445d8580 100644 --- a/packages/api/src/@core/events/events.service.ts +++ b/packages/api/src/@core/events/events.service.ts @@ -1,7 +1,7 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; import { PaginationDto } from '@@core/utils/dtos/pagination.dto'; -import { handleServiceError } from '@@core/utils/errors'; +import { EventsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -36,7 +36,13 @@ export class EventsService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new EventsError( + { + name: "GET_EVENTS_ERROR", + message: "EventsService.findEvents() call failed", + cause: error + } + ), this.logger) } } @@ -44,7 +50,13 @@ export class EventsService { try { return await this.prisma.events.count(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new EventsError( + { + name: "GET_EVENTS_COUNT_ERROR", + message: "EventsService.getEventsCount() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/field-mapping/field-mapping.service.ts b/packages/api/src/@core/field-mapping/field-mapping.service.ts index 91137fd84..08d8c99ac 100644 --- a/packages/api/src/@core/field-mapping/field-mapping.service.ts +++ b/packages/api/src/@core/field-mapping/field-mapping.service.ts @@ -8,7 +8,7 @@ import { } from './dto/create-custom-field.dto'; import { v4 as uuidv4 } from 'uuid'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, CustomFieldsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; import { CrmObject } from '@crm/@lib/@types'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { CONNECTORS_METADATA } from '@panora/shared'; @@ -31,7 +31,13 @@ export class FieldMappingService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "GET_ATTRIBUTES_ERROR", + message: "FieldMappingService.getAttributes() call failed", + cause: error + } + ), this.logger) } } @@ -39,16 +45,27 @@ export class FieldMappingService { try { return await this.prisma.value.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "GET_VALUES_ERROR", + message: "FieldMappingService.getValues() call failed", + cause: error + } + ), this.logger) } - return await this.prisma.value.findMany(); } async getEntities() { try { return await this.prisma.entity.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "GET_ENTITIES_ERROR", + message: "FieldMappingService.getEntities() call failed", + cause: error + } + ), this.logger) } } @@ -70,7 +87,13 @@ export class FieldMappingService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "GET_CUSTOM_FIELDS_ERROR", + message: "FieldMappingService.getCustomFieldMappings() call failed", + cause: error + } + ), this.logger) } } @@ -98,7 +121,13 @@ export class FieldMappingService { return attribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "CREATE_DEFINE_FIELD_ERROR", + message: "FieldMappingService.defineTargetField() call failed", + cause: error + } + ), this.logger) } } @@ -118,7 +147,13 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "CREATE_MAP_FIELD_ERROR", + message: "FieldMappingService.mapFieldToProvider() call failed", + cause: error + } + ), this.logger) } } @@ -154,7 +189,13 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new CustomFieldsError( + { + name: "CREATE_CUSTOM_FIELD_ERROR", + message: "FieldMappingService.createCustomField() call failed", + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/linked-users/linked-users.service.ts b/packages/api/src/@core/linked-users/linked-users.service.ts index 86db512da..59a485b76 100644 --- a/packages/api/src/@core/linked-users/linked-users.service.ts +++ b/packages/api/src/@core/linked-users/linked-users.service.ts @@ -6,7 +6,7 @@ import { import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { handleServiceError, LinkedUserError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class LinkedUsersService { @@ -21,7 +21,13 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new LinkedUserError( + { + name: "GET_LINKED_USERS_ERROR", + message: "LinkedUsersService.getLinkedUsers() call failed", + cause: error + } + ), this.logger) } } async getLinkedUser(id: string) { @@ -32,7 +38,13 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new LinkedUserError( + { + name: "GET_LINKED_USER_ERROR", + message: "LinkedUsersService.getLinkedUser() call failed", + cause: error + } + ), this.logger) } } async getLinkedUserV2(originId: string) { @@ -43,7 +55,13 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new LinkedUserError( + { + name: "GET_LINKED_USER_FROM_REMOTE_ID_ERROR", + message: "LinkedUsersService.getLinkedUserV2() call failed", + cause: error + } + ), this.logger) } } async addLinkedUser(data: CreateLinkedUserDto) { @@ -58,7 +76,13 @@ export class LinkedUsersService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new LinkedUserError( + { + name: "CREATE_LINKED_USER_ERROR", + message: "LinkedUsersService.addlinkedUser() call failed", + cause: error + } + ), this.logger) ; } } async addBatchLinkedUsers(data: CreateBatchLinkedUserDto) { @@ -79,7 +103,13 @@ export class LinkedUsersService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new LinkedUserError( + { + name: "CREATE_BATCH_LINKED_USER_ERROR", + message: "LinkedUsersService.addBatchLinkedUsers() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/magic-link/magic-link.service.ts b/packages/api/src/@core/magic-link/magic-link.service.ts index f6742efbb..89a0fa47f 100644 --- a/packages/api/src/@core/magic-link/magic-link.service.ts +++ b/packages/api/src/@core/magic-link/magic-link.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { CreateMagicLinkDto } from './dto/create-magic-link.dto'; -import { handleServiceError } from '@@core/utils/errors'; +import { MagicLinksError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class MagicLinkService { @@ -15,7 +15,13 @@ export class MagicLinkService { try { return await this.prisma.invite_links.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "GET_MAGIC_LINKS_ERROR", + message: "MagicLinkService.getMagicLinks() call failed", + cause: error + } + ), this.logger) } } @@ -26,17 +32,25 @@ export class MagicLinkService { id_invite_link: id, }, }); + if(!inviteLink) throw new ReferenceError("Magic link undefined") const linkedUser = await this.prisma.linked_users.findFirst({ where: { id_linked_user: inviteLink.id_linked_user, }, }); + if(!linkedUser) throw new ReferenceError("Linked User undefined") return { ...inviteLink, id_project: linkedUser.id_project, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "GET_MAGIC_LINK_ERROR", + message: "MagicLinkService.getMagicLink() call failed", + cause: error + } + ), this.logger) } } @@ -71,7 +85,13 @@ export class MagicLinkService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "CREATE_MAGIC_LINK_ERROR", + message: "MagicLinkService.createUniqueLink() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts index f0366bb6d..aa1e9c10e 100644 --- a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts +++ b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts @@ -1,15 +1,12 @@ import { Injectable } from '@nestjs/common'; -import { Queue } from 'bull'; -import { InjectQueue } from '@nestjs/bull'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { ManagedWebhooksError, throwTypedError } from '@@core/utils/errors'; import { ManagedWebhooksDto, RemoteThirdPartyCreationDto, } from './dto/managed-webhooks.dto'; -import crypto from 'crypto'; import { ConnectorCategory } from '@panora/shared'; import { TicketingWebhookHandlerService } from '@ticketing/@webhook/handler.service'; @@ -31,7 +28,13 @@ export class ManagedWebhooksService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ManagedWebhooksError( + { + name: "GET_MANAGED_WEBHOOKS_ERROR", + message: "ManagedWebhooksService.getManagedWebhook() call failed", + cause: error + } + ), this.logger) } } @@ -42,7 +45,13 @@ export class ManagedWebhooksService { data: { active: active }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ManagedWebhooksError( + { + name: "UPDATE_MANAGED_WEBHOOK_STATUS_ERROR", + message: "ManagedWebhooksService.updateStatusManagedWebhookEndpoint() call failed", + cause: error + } + ), this.logger) } } @@ -62,7 +71,13 @@ export class ManagedWebhooksService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ManagedWebhooksError( + { + name: "CREATE_MANAGED_WEBHOOK_ERROR", + message: "ManagedWebhooksService.createManagedWebhook() call failed", + cause: error + } + ), this.logger) } } @@ -73,6 +88,7 @@ export class ManagedWebhooksService { id_connection: data.id_connection, }, }); + if(!conn) throw new ReferenceError('Connection undefined') switch (conn.vertical) { case ConnectorCategory.Ticketing: return await this.ticketingHandler.createExternalWebhook( @@ -82,7 +98,13 @@ export class ManagedWebhooksService { ); } } catch (error) { - throw new Error(error); + throwTypedError(new ManagedWebhooksError( + { + name: "CREATE_REMOTE_WEBHOOK_ERROR", + message: "ManagedWebhooksService.createRemoteThirdPartyWebhook() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/project-connectors/project-connectors.service.ts b/packages/api/src/@core/project-connectors/project-connectors.service.ts index 394c03fca..93510cfa7 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.service.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.service.ts @@ -2,7 +2,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { ConnectorSetError, handleServiceError, throwTypedError } from '@@core/utils/errors'; import { TypeCustom } from './project-connectors.controller'; @Injectable() @@ -25,6 +25,9 @@ export class ProjectConnectorsService { }, }); + if (!project) { + throw new ReferenceError('Project undefined!'); + } const existingPConnectors = await this.prisma.connector_sets.findFirst({ where: { id_connector_set: project.id_connector_set, @@ -32,8 +35,8 @@ export class ProjectConnectorsService { }); if (!existingPConnectors) { - throw new Error( - `No project connector entry found for project ${id_project}`, + throw new ReferenceError( + `No connector set entry found for project ${id_project}`, ); } @@ -49,7 +52,13 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectorSetError( + { + name: "UPDATE_CONNECTOR_SET_ERROR", + message: "ProjectConnectorsService.updateProjectConnectors() call failed", + cause: error + } + ), this.logger) } } @@ -75,7 +84,13 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectorSetError( + { + name: "CREATE_CONNECTOR_SET_ERROR", + message: "ProjectConnectorsService.createProjectConnectors() call failed", + cause: error + } + ), this.logger) } } @@ -88,7 +103,7 @@ export class ProjectConnectorsService { }); if (!project) { - throw new NotFoundException('Project does not exist!'); + throw new ReferenceError('Project undefined!'); } const res = await this.prisma.connector_sets.findFirst({ @@ -97,13 +112,19 @@ export class ProjectConnectorsService { }, }); if (!res) { - throw new NotFoundException( - 'Connectors not found for current project!', + throw new ReferenceError( + 'Connector set undefined!', ); } return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ConnectorSetError( + { + name: "GET_CONNECTOR_SET_BY_PROJECT_ERROR", + message: "ProjectConnectorsService.getConnectorsbyProjectId() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/projects/projects.service.ts b/packages/api/src/@core/projects/projects.service.ts index 5d0f54362..935623150 100644 --- a/packages/api/src/@core/projects/projects.service.ts +++ b/packages/api/src/@core/projects/projects.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { CreateProjectDto } from './dto/create-project.dto'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { ProjectError, throwTypedError } from '@@core/utils/errors'; import { ConnectorCategory, providersArray, @@ -20,7 +20,13 @@ export class ProjectsService { try { return await this.prisma.projects.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "GET_PROJECTS_ERROR", + message: "ProjectsService.getProjects() call failed", + cause: error + } + ), this.logger) } } @@ -32,12 +38,24 @@ export class ProjectsService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "GET_PROJECT_FOR_USER_ERROR", + message: "ProjectsService.getProjectsByUser() call failed", + cause: error + } + ), this.logger) } } async createProject(data: CreateProjectDto) { try { + const user = await this.prisma.users.findUnique({ + where: { + id_user: data.id_user + } + }) + if(!user) throw ReferenceError("User undefined"); const ACTIVE_CONNECTORS = providersArray(); // update project-connectors table for the project const updateData: any = { @@ -69,7 +87,13 @@ export class ProjectsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "CREATE_PROJECT_ERROR", + message: "ProjectsService.createProject() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/utils/errors.ts b/packages/api/src/@core/utils/errors.ts index e32281d98..94f388339 100644 --- a/packages/api/src/@core/utils/errors.ts +++ b/packages/api/src/@core/utils/errors.ts @@ -5,7 +5,28 @@ import { Prisma } from '@prisma/client'; import { TargetObject } from './types'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -type ServiceError = AxiosError | PrismaClientKnownRequestError | Error; +type TypedError = AxiosError | PrismaClientKnownRequestError | T; + +export class ErrorBase extends Error { + name: T; + message: string; + cause: any; + + constructor({ + name, + message, + cause + }: { + name: T; + message: string; + cause?: any; + }) { + super(); + this.name = name; + this.message = message; + this.cause = cause; + } +} export enum Action { oauthCallback = 'oauth-callback', @@ -19,6 +40,141 @@ export enum ActionType { DELETE = 'DELETE', } +export class ProjectError extends ErrorBase< + "CREATE_PROJECT_ERROR" | + "GET_PROJECT_FOR_USER_ERROR" | + "GET_PROJECTS_ERROR" +>{} + +export class ConnectorSetError extends ErrorBase< + "CREATE_CONNECTOR_SET_ERROR" | + "GET_CONNECTOR_SET_BY_PROJECT_ERROR" | + "UPDATE_CONNECTOR_SET_ERROR" +>{} +export class LinkedUserError extends ErrorBase< + "CREATE_LINKED_USER_ERROR" | + "CREATE_BATCH_LINKED_USER_ERROR" | + "GET_LINKED_USER_FROM_REMOTE_ID_ERROR" | + "GET_LINKED_USERS_ERROR" | + "GET_LINKED_USER_ERROR" +>{} + +export class ConnectionStrategiesError extends ErrorBase< + "CREATE_CONNECTION_STRATEGY_ERROR" | + "DELETE_CONNECTION_STRATEGY_ERROR" | + "UPDATE_CONNECTION_STRATEGY_ERROR" | + "GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR" | + "GET_CONNECTION_STRATEGY_STATUS_ERROR" | + "GET_CREDENTIALS_ERROR" | + "TOGGLE_CONNECTION_STRATEGY_ERROR" | + "CONNECTION_STRATEGY_ALREADY_EXISTS" | + "CUSTOM_CREDENTIALS_ERROR" +>{} + +export class EncryptionError extends ErrorBase< + "ENCRYPT_ERROR" | + "DECRYPT_ERROR" +>{} + +export class EventsError extends ErrorBase< + "GET_EVENTS_ERROR" | + "GET_EVENTS_COUNT_ERROR" +>{} + +export class MagicLinksError extends ErrorBase< + "CREATE_MAGIC_LINK_ERROR" | + "GET_MAGIC_LINKS_ERROR" | + "GET_MAGIC_LINK_ERROR" +>{} + +export class CustomFieldsError extends ErrorBase< + "CREATE_DEFINE_FIELD_ERROR" | + "CREATE_MAP_FIELD_ERROR" | + "CREATE_CUSTOM_FIELD_ERROR" | + "GET_CUSTOM_FIELDS_ERROR" | + "GET_ENTITIES_ERROR" | + "GET_ATTRIBUTES_ERROR" | + "GET_VALUES_ERROR" +>{} + +export class ConnectionsError extends ErrorBase< + "GET_CONNECTIONS_ERROR" | + "OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR" | + "OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR" | + "OAUTH_CALLBACK_ERROR" | + "HANDLE_OAUTH_CALLBACK_TICKETING" | + "HANDLE_OAUTH_CALLBACK_CRM" | + "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION" | + "HANDLE_OAUTH_CALLBACK_FILESTORAGE" | + "HANDLE_OAUTH_CALLBACK_ACCOUNTING" | + "HANDLE_OAUTH_CALLBACK_ATS" | + "HANDLE_OAUTH_CALLBACK_HRIS" | + "HANDLE_OAUTH_REFRESH_TICKETING" | + "HANDLE_OAUTH_REFRESH_CRM" | + "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION" | + "HANDLE_OAUTH_REFRESH_FILESTORAGE" | + "HANDLE_OAUTH_REFRESH_ACCOUNTING" | + "HANDLE_OAUTH_REFRESH_ATS" | + "HANDLE_OAUTH_REFRESH_HRIS" +>{} + +export class ManagedWebhooksError extends ErrorBase< + "GET_MANAGED_WEBHOOKS_ERROR" | + "CREATE_MANAGED_WEBHOOK_ERROR" | + "UPDATE_MANAGED_WEBHOOK_STATUS_ERROR" | + "CREATE_REMOTE_WEBHOOK_ERROR" +>{} + +export class AuthError extends ErrorBase< + "GET_API_KEYS_ERROR" | + "GET_USERS_ERROR" | + "VERIFY_USER_ERROR" | + "LOGIN_USER_ERROR"| + "REGISTER_USER_ERROR" | + "CREATE_USER_ERROR" | + "DELETE_API_KEY_ERROR" | + "REFRESH_ACCESS_TOKEN_ERROR" | + "HASH_API_KEY_ERROR" | + "GENERATE_API_KEY_ERROR" | + "VALIDATE_API_KEY_ERROR" | + "EMAIL_ALREADY_EXISTS_ERROR" +>{} + +export class PassthroughRequestError extends ErrorBase< + "PASSTHROUGH_REMOTE_API_CALL_ERROR" +>{} + +export class WebhooksError extends ErrorBase< + "CREATE_WEBHOOK_ERROR" | + "DELETE_WEBHOOK_ERROR" | + "UPDATE_WEBHOOK_STATUS_ERROR" | + "VERIFY_PAYLOAD_ERROR" | + "GET_WEBHOOKS_ERROR" | + "INVALID_SIGNATURE_ERROR" | + "SIGNATURE_GENERATION_ERROR" +>{} + +export class SyncError extends ErrorBase< + "TICKETING_USER_SYNC_ERROR" | + "TICKETING_TICKET_SYNC_ERROR" | + "TICKETING_ACCOUNT_SYNC_ERROR" | + "TICKETING_CONTACT_SYNC_ERROR" | + "TICKETING_TEAM_SYNC_ERROR" | + "TICKETING_TAG_SYNC_ERROR" | + "TICKETING_COMMENT_SYNC_ERROR" | + "TICKETING_ATTACHMENT_SYNC_ERROR" | + "TICKETING_COLLECTION_SYNC_ERROR" | + "CRM_COMPANY_SYNC_ERROR" | + "CRM_CONTACT_SYNC_ERROR" | + "CRM_DEAL_SYNC_ERROR" | + "CRM_ENGAGEMENT_SYNC_ERROR" | + "CRM_NOTE_SYNC_ERROR" | + "CRM_STAGE_SYNC_ERROR" | + "CRM_TASK_SYNC_ERROR" | + "CRM_USER_SYNC_ERROR" +>{} + +/* */ // Custom error for general application errors export class AppError extends Error { constructor(message: string) { @@ -59,8 +215,49 @@ export class NotUniqueRecord extends Error { } } +export class ThirdPartyApiCallError extends Error { + constructor(message: string) { + super(message); + this.name = 'ThirdPartyApiCallError'; + } +} + +export class PrismaError extends Error { + constructor(message: string) { + super(message); + this.name = 'PrismaError'; + } +} + +export function throwTypedError( + error: TypedError, + logger: LoggerService, +) { + let errorMessage = error.message; + logger.error('Error message: ', errorMessage); + throw error; +} + +export function format3rdPartyError( + providerName: string, + action: TargetObject | Action, + actionType: ActionType, +){ + switch(actionType) { + case 'POST': + return `Failed to create ${action} for ${providerName}` + case 'GET': + return `Failed to retrieve ${action} for ${providerName}` + case 'PUT': + return `Failed to update ${action} for ${providerName}` + case 'DELETE': + return `Failed to delete ${action} for ${providerName}` + } +} + + export function handleServiceError( - error: ServiceError, + error: TypedError, logger: LoggerService, providerName?: string, action?: TargetObject | Action, @@ -73,32 +270,29 @@ export function handleServiceError( statusCode = error.response?.status || 500; // Use HTTP status code from Axios error or default to 500 errorMessage = error.response?.data || error.message; logger.error('Error with Axios request:', errorMessage); - } else if (error instanceof Prisma.PrismaClientKnownRequestError) { - // Handle Prisma errors + } + + if (error instanceof Prisma.PrismaClientKnownRequestError) { logger.error('Error with Prisma request:', errorMessage); - throw new Error(errorMessage); - } else { - logger.error('An unknown error occurred...', errorMessage); - throw new Error(errorMessage); - } + throw new PrismaError(errorMessage); + } if (error instanceof HttpException) { throw error; } - return { - data: null, - error: errorMessage, - message: - action && providerName && actionType - ? actionType == 'POST' - ? `Failed to create ${action} for ${providerName}.` - : actionType == 'GET' - ? `Failed to retrieve ${action} for ${providerName}.` - : actionType == 'PUT' - ? `Failed to update ${action} for ${providerName}.` - : `Failed to delete ${action} for ${providerName}.` - : errorMessage, - statusCode: statusCode, - }; + const message = + action && providerName && actionType + ? actionType == 'POST' + ? `Failed to create ${action} for ${providerName} : ${errorMessage}` + : actionType == 'GET' + ? `Failed to retrieve ${action} for ${providerName} : ${errorMessage}` + : actionType == 'PUT' + ? `Failed to update ${action} for ${providerName} : ${errorMessage}` + : `Failed to delete ${action} for ${providerName} : ${errorMessage}` + : errorMessage; + + throw new ThirdPartyApiCallError( + message + ) } diff --git a/packages/api/src/@core/webhook/webhook.service.ts b/packages/api/src/@core/webhook/webhook.service.ts index 83d4725a8..4a50fd9bb 100644 --- a/packages/api/src/@core/webhook/webhook.service.ts +++ b/packages/api/src/@core/webhook/webhook.service.ts @@ -4,7 +4,7 @@ import { InjectQueue } from '@nestjs/bull'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { handleServiceError, throwTypedError, WebhooksError } from '@@core/utils/errors'; import { WebhookDto } from './dto/webhook.dto'; import axios from 'axios'; import crypto from 'crypto'; @@ -20,10 +20,20 @@ export class WebhookService { } generateSignature(payload: any, secret: string): string { - return crypto + try{ + return crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); + }catch(error){ + throwTypedError(new WebhooksError( + { + name: "SIGNATURE_GENERATION_ERROR", + message: "WebhookService.generateSignature() call failed", + cause: error + } + ), this.logger) + } } async getWebhookEndpoints(project_id: string) { @@ -34,9 +44,16 @@ export class WebhookService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new WebhooksError( + { + name: "GET_WEBHOOKS_ERROR", + message: "WebhookService.getWebhookEndpoints() call failed", + cause: error + } + ), this.logger) } } + async updateStatusWebhookEndpoint(id: string, active: boolean) { try { return await this.prisma.webhook_endpoints.update({ @@ -44,7 +61,13 @@ export class WebhookService { data: { active: active }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new WebhooksError( + { + name: "UPDATE_WEBHOOK_STATUS_ERROR", + message: "WebhookService.updateStatusWebhookEndpoint() call failed", + cause: error + } + ), this.logger) } } @@ -63,16 +86,32 @@ export class WebhookService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new WebhooksError( + { + name: "CREATE_WEBHOOK_ERROR", + message: "WebhookService.createWebhookEndpoint() call failed", + cause: error + } + ), this.logger) } } async deleteWebhook(whId: string) { - return await this.prisma.webhook_endpoints.delete({ - where: { - id_webhook_endpoint: whId, - }, - }); + try{ + return await this.prisma.webhook_endpoints.delete({ + where: { + id_webhook_endpoint: whId, + }, + }); + }catch(error){ + throwTypedError(new WebhooksError( + { + name: "DELETE_WEBHOOK_ERROR", + message: "WebhookService.deleteWebhook() call failed", + cause: error + } + ), this.logger) + } } async handleWebhook( @@ -276,11 +315,22 @@ export class WebhookService { try { const expected = this.generateSignature(payload, secret); if (expected !== signature) { - throw new Error('Invalid signature'); + throw new WebhooksError( + { + name: "INVALID_SIGNATURE_ERROR", + message: `Signature mismatch for the payload received with signature=${signature}`, + } + ) } return 200; } catch (error) { - throw new Error(error); + throwTypedError(new WebhooksError( + { + name: "VERIFY_PAYLOAD_ERROR", + message: "WebhookService.verifyPayloadSignature() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/ticketing/user/sync/sync.service.ts b/packages/api/src/ticketing/user/sync/sync.service.ts index 06129515d..02d79089e 100644 --- a/packages/api/src/ticketing/user/sync/sync.service.ts +++ b/packages/api/src/ticketing/user/sync/sync.service.ts @@ -40,23 +40,27 @@ export class SyncService implements OnModuleInit { } private async scheduleSyncJob() { - const jobName = 'ticketing-sync-users'; + try{ + const jobName = 'ticketing-sync-users'; - // Remove existing jobs to avoid duplicates in case of application restart - const jobs = await this.syncQueue.getRepeatableJobs(); - for (const job of jobs) { - if (job.name === jobName) { - await this.syncQueue.removeRepeatableByKey(job.key); + // Remove existing jobs to avoid duplicates in case of application restart + const jobs = await this.syncQueue.getRepeatableJobs(); + for (const job of jobs) { + if (job.name === jobName) { + await this.syncQueue.removeRepeatableByKey(job.key); + } } + // Add new job to the queue with a CRON expression + await this.syncQueue.add( + jobName, + {}, + { + repeat: { cron: '0 0 * * *' }, // Runs once a day at midnight + }, + ); + }catch(error){ + throw new Error(error); } - // Add new job to the queue with a CRON expression - await this.syncQueue.add( - jobName, - {}, - { - repeat: { cron: '0 0 * * *' }, // Runs once a day at midnight - }, - ); } //function used by sync worker which populate our tcg_users table //its role is to fetch all users from providers 3rd parties and save the info inside our db diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index ccf6b07f1..03177ba33 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -365,20 +365,6 @@ ] } }, - "/connections/create": { - "post": { - "operationId": "ConnectionsController_createConnection", - "parameters": [], - "responses": { - "201": { - "description": "" - } - }, - "tags": [ - "connections" - ] - } - }, "/webhook": { "get": { "operationId": "getWebhooksMetadata", From 0a8f4472fde2204d98d3f86365e312afdd88db6f Mon Sep 17 00:00:00 2001 From: nael Date: Thu, 13 Jun 2024 20:53:52 +0200 Subject: [PATCH 4/9] :ambulance: Errors fixing new format --- packages/api/scripts/commonObject.sh | 54 ++- packages/api/scripts/connectorUpdate.js | 59 ++- packages/api/scripts/oauthConnector.js | 42 +- packages/api/src/@core/auth/auth.service.ts | 221 ++++----- .../api/src/@core/connections/@utils/index.ts | 14 +- .../services/pennylane/pennylane.service.ts | 42 +- .../accounting/services/registry.service.ts | 4 +- .../connections/connections.controller.ts | 67 +-- .../crm/services/accelo/accelo.service.ts | 42 +- .../crm/services/registry.service.ts | 4 +- .../services/registry.service.ts | 4 +- .../ticketing/services/registry.service.ts | 4 +- .../api/src/@core/events/events.service.ts | 32 +- .../field-mapping/field-mapping.service.ts | 127 +++--- .../linked-users/linked-users.service.ts | 77 ++-- .../managed-webhooks.service.ts | 64 +-- .../organisations/organisations.service.ts | 3 - .../@core/passthrough/passthrough.service.ts | 11 +- .../project-connectors.service.ts | 56 +-- packages/api/src/@core/sync/sync.service.ts | 211 +++++++-- packages/api/src/@core/utils/errors.ts | 428 +++++++++--------- .../utils/services/validateUser.service.ts | 5 +- .../api/src/@core/utils/unification/unify.ts | 1 - .../api/src/@core/webhook/webhook.service.ts | 161 ++++--- packages/api/src/crm/@lib/@utils/index.ts | 62 +-- .../src/crm/company/services/attio/index.ts | 6 +- .../src/crm/company/services/close/index.ts | 6 +- .../crm/company/services/company.service.ts | 40 +- .../src/crm/company/services/hubspot/index.ts | 6 +- .../crm/company/services/pipedrive/index.ts | 6 +- .../src/crm/company/services/zendesk/index.ts | 7 +- .../src/crm/company/services/zoho/index.ts | 7 +- .../api/src/crm/company/sync/sync.service.ts | 27 +- .../src/crm/contact/services/attio/index.ts | 7 +- .../src/crm/contact/services/close/index.ts | 6 +- .../crm/contact/services/contact.service.ts | 38 +- .../src/crm/contact/services/hubspot/index.ts | 6 +- .../crm/contact/services/pipedrive/index.ts | 7 +- .../crm/contact/services/registry.service.ts | 4 +- .../src/crm/contact/services/zendesk/index.ts | 7 +- .../src/crm/contact/services/zoho/index.ts | 7 +- .../api/src/crm/contact/sync/sync.service.ts | 27 +- .../api/src/crm/deal/services/close/index.ts | 6 +- .../api/src/crm/deal/services/deal.service.ts | 44 +- .../src/crm/deal/services/hubspot/index.ts | 6 +- .../src/crm/deal/services/pipedrive/index.ts | 7 +- .../src/crm/deal/services/zendesk/index.ts | 7 +- .../api/src/crm/deal/services/zoho/index.ts | 7 +- .../api/src/crm/deal/sync/sync.service.ts | 27 +- .../crm/engagement/services/close/index.ts | 18 +- .../engagement/services/engagement.service.ts | 46 +- .../crm/engagement/services/hubspot/index.ts | 18 +- .../engagement/services/pipedrive/index.ts | 12 +- .../crm/engagement/services/zendesk/index.ts | 10 +- .../src/crm/engagement/services/zoho/index.ts | 7 +- .../src/crm/engagement/sync/sync.service.ts | 38 +- .../api/src/crm/note/services/close/index.ts | 6 +- .../src/crm/note/services/hubspot/index.ts | 6 +- .../api/src/crm/note/services/note.service.ts | 52 ++- .../src/crm/note/services/pipedrive/index.ts | 7 +- .../src/crm/note/services/zendesk/index.ts | 7 +- .../api/src/crm/note/services/zoho/index.ts | 7 +- .../api/src/crm/note/sync/sync.service.ts | 39 +- .../api/src/crm/stage/services/close/index.ts | 4 +- .../src/crm/stage/services/hubspot/index.ts | 4 +- .../src/crm/stage/services/pipedrive/index.ts | 4 +- .../src/crm/stage/services/stage.service.ts | 21 +- .../src/crm/stage/services/zendesk/index.ts | 4 +- .../api/src/crm/stage/services/zoho/index.ts | 4 +- .../api/src/crm/stage/sync/sync.service.ts | 41 +- .../api/src/crm/task/services/close/index.ts | 6 +- .../src/crm/task/services/hubspot/index.ts | 6 +- .../src/crm/task/services/pipedrive/index.ts | 7 +- .../api/src/crm/task/services/task.service.ts | 48 +- .../src/crm/task/services/zendesk/index.ts | 7 +- .../api/src/crm/task/services/zoho/index.ts | 7 +- .../api/src/crm/task/sync/sync.service.ts | 39 +- .../api/src/crm/user/services/close/index.ts | 4 +- .../src/crm/user/services/hubspot/index.ts | 4 +- .../src/crm/user/services/pipedrive/index.ts | 4 +- .../api/src/crm/user/services/user.service.ts | 18 +- .../src/crm/user/services/zendesk/index.ts | 4 +- .../api/src/crm/user/services/zoho/index.ts | 4 +- .../api/src/crm/user/sync/sync.service.ts | 39 +- .../api/src/ticketing/@lib/@utils/index.ts | 58 +-- .../src/ticketing/@webhook/zendesk/handler.ts | 428 ++++++++++-------- .../account/services/account.service.ts | 18 +- .../ticketing/account/services/front/index.ts | 6 +- .../account/services/zendesk/index.ts | 6 +- .../ticketing/account/sync/sync.service.ts | 39 +- .../attachment/services/attachment.service.ts | 36 +- .../attachment/services/registry.service.ts | 4 +- .../collection/services/collection.service.ts | 14 +- .../collection/services/gitlab/index.ts | 135 +++--- .../collection/services/jira/index.ts | 9 +- .../ticketing/collection/sync/sync.service.ts | 42 +- .../comment/services/comment.service.ts | 54 ++- .../ticketing/comment/services/front/index.ts | 16 +- .../comment/services/gitlab/index.ts | 376 +++++++-------- .../comment/services/gorgias/index.ts | 16 +- .../ticketing/comment/services/jira/index.ts | 16 +- .../comment/services/registry.service.ts | 4 +- .../comment/services/zendesk/index.ts | 14 +- .../ticketing/comment/sync/sync.service.ts | 51 ++- .../contact/services/contact.service.ts | 14 +- .../ticketing/contact/services/front/index.ts | 9 +- .../contact/services/gorgias/index.ts | 9 +- .../contact/services/registry.service.ts | 4 +- .../contact/services/zendesk/index.ts | 6 +- .../ticketing/contact/sync/sync.service.ts | 37 +- .../src/ticketing/tag/services/front/index.ts | 6 +- .../ticketing/tag/services/gorgias/index.ts | 6 +- .../src/ticketing/tag/services/jira/index.ts | 6 +- .../src/ticketing/tag/services/tag.service.ts | 14 +- .../ticketing/tag/services/zendesk/index.ts | 6 +- .../src/ticketing/tag/sync/sync.service.ts | 38 +- .../ticketing/team/services/front/index.ts | 6 +- .../ticketing/team/services/gorgias/index.ts | 6 +- .../src/ticketing/team/services/jira/index.ts | 6 +- .../ticketing/team/services/team.service.ts | 14 +- .../ticketing/team/services/zendesk/index.ts | 6 +- .../src/ticketing/team/sync/sync.service.ts | 39 +- .../ticketing/ticket/services/front/index.ts | 14 +- .../ticketing/ticket/services/github/index.ts | 10 +- .../ticketing/ticket/services/gitlab/index.ts | 176 +++---- .../ticket/services/gorgias/index.ts | 14 +- .../ticket/services/hubspot/index.ts | 10 +- .../ticketing/ticket/services/jira/index.ts | 12 +- .../ticketing/ticket/services/jira/mappers.ts | 4 +- .../ticket/services/registry.service.ts | 4 +- .../ticket/services/ticket.service.ts | 52 ++- .../ticket/services/zendesk/index.ts | 14 +- .../src/ticketing/ticket/sync/sync.service.ts | 27 +- .../ticketing/user/services/front/index.ts | 6 +- .../ticketing/user/services/gitlab/index.ts | 94 ++-- .../ticketing/user/services/gorgias/index.ts | 6 +- .../src/ticketing/user/services/jira/index.ts | 6 +- .../ticketing/user/services/user.service.ts | 16 +- .../ticketing/user/services/zendesk/index.ts | 6 +- .../src/ticketing/user/sync/sync.service.ts | 45 +- .../api/src/ticketing/user/user.controller.ts | 14 +- packages/shared/src/authUrl.ts | 6 +- packages/shared/src/envConfig.ts | 2 +- 143 files changed, 2830 insertions(+), 1947 deletions(-) diff --git a/packages/api/scripts/commonObject.sh b/packages/api/scripts/commonObject.sh index 089ca549f..45da3c535 100755 --- a/packages/api/scripts/commonObject.sh +++ b/packages/api/scripts/commonObject.sh @@ -51,12 +51,12 @@ export class ServiceRegistry { getService(integrationId: string): I${ObjectCap}Service { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError(`Service not found for integration ID: ${integrationId}`); } return service; } } -EOF +EOF cat > "services/${objectType}.service.ts" < = await service.add${ObjectCap}( @@ -348,7 +354,13 @@ export class ${ObjectCap}Service { ); return result_${objectType}; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'CREATE_${ObjectCap}_ERROR', + message: '${ObjectCap}Service.add${ObjectCap}() call failed', + cause: error, + }), + ); } } @@ -421,7 +433,13 @@ export class ${ObjectCap}Service { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'GET_${ObjectCap}_ERROR', + message: '${ObjectCap}Service.get${ObjectCap}() call failed', + cause: error, + }), + ); } } @@ -513,7 +531,13 @@ export class ${ObjectCap}Service { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'GET_${ObjectCap}S_ERROR', + message: '${ObjectCap}Service.get${ObjectCap}s() call failed', + cause: error, + }), + ); } } } @@ -523,7 +547,7 @@ cat > "sync/sync.service.ts" < { const name = provider.substring(0, 1).toUpperCase() + provider.substring(1) + - objectType.substring(0, 1).toUpperCase() + objectType.substring(1); + objectType.substring(0, 1).toUpperCase() + + objectType.substring(1); const inputObjPattern = new RegExp(`(${name}Input)`); const outputObjPattern = new RegExp(`(${name}Output)`); - if (inputObjPattern.test(fileContent) || outputObjPattern.test(fileContent)) { - return false + if ( + inputObjPattern.test(fileContent) || + outputObjPattern.test(fileContent) + ) { + return false; } return true; }); return possibleImports; - } // Function to generate import statements for new service types function generateImportStatements(possibleProviders, basePath, objectType) { - return possibleProviders.map((serviceName) => { const importPath = `${basePath}/${serviceName}/types`; const name = serviceName.substring(0, 1).toUpperCase() + serviceName.substring(1) + - objectType.substring(0, 1).toUpperCase() + objectType.substring(1); + objectType.substring(0, 1).toUpperCase() + + objectType.substring(1); return `import { ${name}Input, ${name}Output } from '${replaceRelativePaths( importPath, )}';`; @@ -85,9 +92,14 @@ function updateTargetFile(file, importStatements, serviceNames, objectType) { // Update OriginalObjectTypeInput // checking whether OriginalObjectTypeInput assigns null - const inputNullRegex = new RegExp(`(export type Original${objectType}Input = null)`); + const inputNullRegex = new RegExp( + `(export type Original${objectType}Input = null)`, + ); if (inputNullRegex.test(fileContent)) { - fileContent = fileContent.replace(inputNullRegex, `export type Original${objectType}Input =`) + fileContent = fileContent.replace( + inputNullRegex, + `export type Original${objectType}Input =`, + ); } const inputRegex = new RegExp(`(export type Original${objectType}Input =)`); if (inputRegex.test(fileContent)) { @@ -100,9 +112,14 @@ function updateTargetFile(file, importStatements, serviceNames, objectType) { // Update OriginalObjectTypeOutput // checking whether OriginalObjectTypeInput assigns null - const outputNullRegex = new RegExp(`(export type Original${objectType}Input = null)`); + const outputNullRegex = new RegExp( + `(export type Original${objectType}Input = null)`, + ); if (outputNullRegex.test(fileContent)) { - fileContent = fileContent.replace(outputNullRegex, `export type Original${objectType}Output =`) + fileContent = fileContent.replace( + outputNullRegex, + `export type Original${objectType}Output =`, + ); } const outputRegex = new RegExp( @@ -330,14 +347,15 @@ function updateObjectTypes(baseDir, objectType, vertical) { __dirname, '../../shared/src/connectors/index.ts', ); - const enumFilePath = path.join(__dirname, '../../shared/src/connectors/enum.ts'); + const enumFilePath = path.join( + __dirname, + '../../shared/src/connectors/enum.ts', + ); const currentProviders = extractArrayFromFile( providersFilePath, `${vertical.toUpperCase()}_PROVIDERS`, ); - - // Compare the extracted arrays with the new service names const newProviders = newServiceDirs.filter( (service) => !currentProviders.includes(service), @@ -369,7 +387,11 @@ function updateObjectTypes(baseDir, objectType, vertical) { // Call updateMappingsFile to update the mappings file with new services updateMappingsFile(mappingsFile, newServiceDirs, objectType, vertical); - const possibleProviderForImportStatements = getProvidersForImportStatements(targetFile, newServiceDirs, objectType); + const possibleProviderForImportStatements = getProvidersForImportStatements( + targetFile, + newServiceDirs, + objectType, + ); // Continue with the rest of the updateObjectTypes function... const importStatements = generateImportStatements( @@ -378,7 +400,12 @@ function updateObjectTypes(baseDir, objectType, vertical) { objectType, ); // console.log(importStatements) - updateTargetFile(targetFile, importStatements, possibleProviderForImportStatements, objectType); + updateTargetFile( + targetFile, + importStatements, + possibleProviderForImportStatements, + objectType, + ); } // Example usage for ticketing/team diff --git a/packages/api/scripts/oauthConnector.js b/packages/api/scripts/oauthConnector.js index fee1bd590..5f4256696 100755 --- a/packages/api/scripts/oauthConnector.js +++ b/packages/api/scripts/oauthConnector.js @@ -35,10 +35,16 @@ function createServiceFile(vertical, provider) { // Define the content of the service file const serviceFileContent = ` -import { Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -160,7 +166,18 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne } return db_res; } catch (error) { - handleServiceError(error, this.logger, '${provider}', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_${verticalUpper}', + message: \`${providerUpper}ConnectionService.handleCallback() call failed ---> \${format3rdPartyError( + '${provider}', + Action.oauthCallback, + ActionType.POST, + )}\`, + cause: error, + }), + this.logger, + ); } } @@ -181,10 +198,8 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne { headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: \`Basic ${Buffer.from( - `CREDENTIALS.CLIENT_ID}:\${ - CREDENTIALS.CLIENT_SECRET - }`, + Authorization: \`Basic \${Buffer.from( + \`\${CREDENTIALS.CLIENT_ID}:\${CREDENTIALS.CLIENT_SECRET}\`, ).toString('base64')}\`, }, }, @@ -204,7 +219,18 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne }); this.logger.log('OAuth credentials updated : ${provider} '); } catch (error) { - handleServiceError(error, this.logger, '${provider}', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_${verticalUpper}', + message: \`${providerUpper}ConnectionService.handleTokenRefresh() call failed ---> \${format3rdPartyError( + '${provider}', + Action.oauthCallback, + ActionType.POST, + )}\`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/auth/auth.service.ts b/packages/api/src/@core/auth/auth.service.ts index 1ef8c5d60..bb9ae6b85 100644 --- a/packages/api/src/@core/auth/auth.service.ts +++ b/packages/api/src/@core/auth/auth.service.ts @@ -1,6 +1,4 @@ -import { - Injectable, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { CreateUserDto } from './dto/create-user.dto'; import { PrismaService } from '../prisma/prisma.service'; @@ -28,13 +26,14 @@ export class AuthService { try { return await this.prisma.users.findMany(); } catch (error) { - throwTypedError(new AuthError( - { - name: "GET_USERS_ERROR", - message: "AuthService.getUsers() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'GET_USERS_ERROR', + message: 'AuthService.getUsers() call failed', + cause: error, + }), + this.logger, + ); } } @@ -52,13 +51,14 @@ export class AuthService { return verifyUser; } catch (error) { - throwTypedError(new AuthError( - { - name: "VERIFY_USER_ERROR", - message: "AuthService.verifyUser() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'VERIFY_USER_ERROR', + message: 'AuthService.verifyUser() call failed', + cause: error, + }), + this.logger, + ); } } @@ -70,13 +70,14 @@ export class AuthService { }, }); } catch (error) { - throwTypedError(new AuthError( - { - name: "GET_API_KEYS_ERROR", - message: "AuthService.getApiKeys() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'GET_API_KEYS_ERROR', + message: 'AuthService.getApiKeys() call failed', + cause: error, + }), + this.logger, + ); } } @@ -87,23 +88,22 @@ export class AuthService { }); if (foundUser) { - new AuthError( - { - name: "EMAIL_ALREADY_EXISTS_ERROR", - message: `Email already exists for user with email=${user.email}`, - } - ) + new AuthError({ + name: 'EMAIL_ALREADY_EXISTS_ERROR', + message: `Email already exists for user with email=${user.email}`, + }); } return await this.createUser(user); } catch (error) { - throwTypedError(new AuthError( - { - name: "REGISTER_USER_ERROR", - message: "AuthService.register() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'REGISTER_USER_ERROR', + message: 'AuthService.register() call failed', + cause: error, + }), + this.logger, + ); } } @@ -125,16 +125,17 @@ export class AuthService { await this.projectService.createProject({ name: 'Project 1', id_user: user_.id_user, - }); + }); return user_; } catch (error) { - throwTypedError(new AuthError( - { - name: "CREATE_USER_ERROR", - message: "AuthService.createUser() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'CREATE_USER_ERROR', + message: 'AuthService.createUser() call failed', + cause: error, + }), + this.logger, + ); } } @@ -165,7 +166,10 @@ export class AuthService { foundUser.password_hash, ); - if (!isEq) throw new ReferenceError('Bcrypt Invalid credentials, mismatch in password.'); + if (!isEq) + throw new ReferenceError( + 'Bcrypt Invalid credentials, mismatch in password.', + ); const { ...userData } = foundUser; @@ -189,13 +193,14 @@ export class AuthService { }), // token used to generate api keys }; } catch (error) { - throwTypedError(new AuthError( - { - name: "LOGIN_USER_ERROR", - message: "AuthService.login() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'LOGIN_USER_ERROR', + message: 'AuthService.login() call failed', + cause: error, + }), + this.logger, + ); } } @@ -220,27 +225,29 @@ export class AuthService { }), }; } catch (error) { - throwTypedError(new AuthError( - { - name: "REFRESH_ACCESS_TOKEN_ERROR", - message: "AuthService.refreshAccessToken() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'REFRESH_ACCESS_TOKEN_ERROR', + message: 'AuthService.refreshAccessToken() call failed', + cause: error, + }), + this.logger, + ); } } hashApiKey(apiKey: string): string { - try{ + try { return crypto.createHash('sha256').update(apiKey).digest('hex'); - }catch(error){ - throwTypedError(new AuthError( - { - name: "HASH_API_KEY_ERROR", - message: "AuthService.hashApiKey() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new AuthError({ + name: 'HASH_API_KEY_ERROR', + message: 'AuthService.hashApiKey() call failed', + cause: error, + }), + this.logger, + ); } } @@ -261,13 +268,14 @@ export class AuthService { }), }; } catch (error) { - throwTypedError(new AuthError( - { - name: "GENERATE_API_KEY_ERROR", - message: "AuthService.generateApiKey() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'GENERATE_API_KEY_ERROR', + message: 'AuthService.generateApiKey() call failed', + cause: error, + }), + this.logger, + ); } } @@ -281,21 +289,17 @@ export class AuthService { where: { id_project: projectId }, }); if (!foundProject) { - throw new ReferenceError( - 'project undefined', - ); + throw new ReferenceError('project undefined'); } const foundUser = await this.prisma.users.findUnique({ where: { id_user: userId }, }); if (!foundUser) { - throw new ReferenceError( - 'user undefined', - ); + throw new ReferenceError('user undefined'); } /*if (foundProject.id_organization !== foundUser.id_organization) { - throw new Error('User is not inside the project'); + throw new ReferenceError('User is not inside the project'); }*/ // Generate a new API key (use a secure method for generation) const { access_token } = await this.generateApiKey(projectId, userId); @@ -316,31 +320,33 @@ export class AuthService { return { api_key: access_token, ...new_api_key }; } catch (error) { - throwTypedError(new AuthError( - { - name: "GENERATE_API_KEY_ERROR", - message: "AuthService.generateApiKeyForUser() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'GENERATE_API_KEY_ERROR', + message: 'AuthService.generateApiKeyForUser() call failed', + cause: error, + }), + this.logger, + ); } } async deleteApiKey(apiKeyId: string) { - try{ + try { return await this.prisma.api_keys.delete({ where: { id_api_key: apiKeyId, }, }); - }catch(error){ - throwTypedError(new AuthError( - { - name: "DELETE_API_KEY_ERROR", - message: "AuthService.deleteApiKey() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new AuthError({ + name: 'DELETE_API_KEY_ERROR', + message: 'AuthService.deleteApiKey() call failed', + cause: error, + }), + this.logger, + ); } } @@ -357,7 +363,7 @@ export class AuthService { api_key_hash: apiKey, }, }); - + if (!saved_api_key) { throw new ReferenceError('Api Key undefined'); } @@ -375,13 +381,14 @@ export class AuthService { } return true; } catch (error) { - throwTypedError(new AuthError( - { - name: "VALIDATE_API_KEY_ERROR", - message: "AuthService.validateApiKey() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new AuthError({ + name: 'VALIDATE_API_KEY_ERROR', + message: 'AuthService.validateApiKey() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/@utils/index.ts b/packages/api/src/@core/connections/@utils/index.ts index f2c078cf7..7d1f9777d 100644 --- a/packages/api/src/@core/connections/@utils/index.ts +++ b/packages/api/src/@core/connections/@utils/index.ts @@ -1,3 +1,4 @@ +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { PrismaClient } from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; @@ -17,21 +18,24 @@ export class ConnectionUtils { token: string, ): Promise { try { - // console.log('token is ' + token); - if (!token) - throw new Error('token provided for connection token is invalid'); const res = await this.prisma.connections.findFirst({ where: { connection_token: token, }, }); - if (!res) throw new Error(`connection not found for token ${token}`); + if (!res) throw new ReferenceError(`Connection undefined for token ${token}`); return { linkedUserId: res.id_linked_user, remoteSource: res.provider_slug, }; } catch (error) { - throw new Error(error); + throwTypedError(new ConnectionsError( + { + name: "GET_CONNECTION_FROM_CONNECTION_TOKEN_ERROR", + message: "ConnectionUtils.getConnectionMetadataFromConnectionToken() call failed", + cause: error + } + )) } } diff --git a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts index 6c4b321e1..d6a633255 100644 --- a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts +++ b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts @@ -1,7 +1,13 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, ActionType, ConnectionsError, format3rdPartyError, handleServiceError, throwTypedError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,17 +142,18 @@ export class PennylaneConnectionService } return db_res; } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_ACCOUNTING', message: `PennylaneConnectionService.handleCallback() call failed ---> ${format3rdPartyError( - "pennylane", + 'pennylane', Action.oauthCallback, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } @@ -189,17 +196,18 @@ export class PennylaneConnectionService }); this.logger.log('OAuth credentials updated : pennylane '); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_ACCOUNTING', message: `PennylaneConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( - "pennylane", + 'pennylane', Action.oauthRefresh, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/accounting/services/registry.service.ts b/packages/api/src/@core/connections/accounting/services/registry.service.ts index 3125b3a83..5c614b98e 100644 --- a/packages/api/src/@core/connections/accounting/services/registry.service.ts +++ b/packages/api/src/@core/connections/accounting/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IAccountingConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 23706f107..e4c2cabd0 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -9,7 +9,7 @@ import { import { Response } from 'express'; import { CrmConnectionsService } from './crm/services/crm.connection.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ConnectionsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { PrismaService } from '@@core/prisma/prisma.service'; import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; import { TicketingConnectionsService } from './ticketing/services/ticketing.connection.service'; @@ -37,7 +37,7 @@ export class ConnectionsController { private readonly marketingAutomationConnectionsService: MarketingAutomationConnectionsService, private logger: LoggerService, private prisma: PrismaService, - private coreSync: CoreSyncService + private coreSync: CoreSyncService, ) { this.logger.setContext(ConnectionsController.name); } @@ -59,19 +59,15 @@ export class ConnectionsController { ) { try { if (!state) - new ConnectionsError( - { - name: "OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR", + new ConnectionsError({ + name: 'OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR', message: `No Callback Params found for state, found ${state}`, - } - ) + }); if (!code) - new ConnectionsError( - { - name: "OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR", - message: `No Callback Params found for code, found ${code}`, - } - ) + new ConnectionsError({ + name: 'OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR', + message: `No Callback Params found for code, found ${code}`, + }); const stateData: StateDataType = JSON.parse(decodeURIComponent(state)); const { projectId, vertical, linkedUserId, providerName, returnUrl } = @@ -127,13 +123,14 @@ export class ConnectionsController { ); res.redirect(returnUrl); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "OAUTH_CALLBACK_ERROR", - message: "ConnectionsController.handleCallback() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'OAUTH_CALLBACK_ERROR', + message: 'ConnectionsController.handleCallback() call failed', + cause: error, + }), + this.logger, + ); } } @@ -149,11 +146,18 @@ export class ConnectionsController { @Query('state') state: string, ) { try { - if (!account) throw new Error('account prop not found'); + if (!account) throw new ReferenceError('account prop not found'); const params = `?client_id=${client_id}&response_type=${response_type}&redirect_uri=${redirect_uri}&state=${state}&nonce=${nonce}&scope=${scope}`; res.redirect(`https://${account}.gorgias.com/oauth/authorize${params}`); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'OAUTH_CALLBACK_ERROR', + message: 'ConnectionsController.handleGorgiasAuthUrl() call failed', + cause: error, + }), + this.logger, + ); } } @@ -165,7 +169,7 @@ export class ConnectionsController { @UseGuards(JwtAuthGuard) @Get() async getConnections(@Request() req: any) { - try{ + try { const { id_project } = req.user; console.log('Req data is:', req.user); return await this.prisma.connections.findMany({ @@ -173,14 +177,15 @@ export class ConnectionsController { id_project: id_project, }, }); - }catch(error){ - throwTypedError(new ConnectionsError( - { - name: "GET_CONNECTIONS_ERROR", - message: "ConnectionsController.getConnections() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new ConnectionsError({ + name: 'GET_CONNECTIONS_ERROR', + message: 'ConnectionsController.getConnections() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts index 255a66e00..4ca796d81 100644 --- a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts +++ b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts @@ -7,7 +7,13 @@ import { import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from '../registry.service'; @@ -144,17 +150,18 @@ export class AcceloConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_CRM", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', message: `AcceloConnectionService.handleCallback() call failed ---> ${format3rdPartyError( - "accelo", + 'accelo', Action.oauthCallback, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } @@ -199,17 +206,18 @@ export class AcceloConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : accelo '); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_CRM", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', message: `AcceloConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( - "accelo", + 'accelo', Action.oauthRefresh, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/registry.service.ts b/packages/api/src/@core/connections/crm/services/registry.service.ts index cec95d9c7..bbea65b82 100644 --- a/packages/api/src/@core/connections/crm/services/registry.service.ts +++ b/packages/api/src/@core/connections/crm/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ICrmConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts b/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts index b907b5f03..36412009e 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts @@ -19,7 +19,9 @@ export class ServiceRegistry { getService(integrationId: string): IMarketingAutomationConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/ticketing/services/registry.service.ts b/packages/api/src/@core/connections/ticketing/services/registry.service.ts index 0de0f679d..a1192883a 100644 --- a/packages/api/src/@core/connections/ticketing/services/registry.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ITicketingConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/events/events.service.ts b/packages/api/src/@core/events/events.service.ts index 7445d8580..8aab5304c 100644 --- a/packages/api/src/@core/events/events.service.ts +++ b/packages/api/src/@core/events/events.service.ts @@ -1,7 +1,7 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; import { PaginationDto } from '@@core/utils/dtos/pagination.dto'; -import { EventsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; +import { EventsError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -36,13 +36,14 @@ export class EventsService { }, }); } catch (error) { - throwTypedError(new EventsError( - { - name: "GET_EVENTS_ERROR", - message: "EventsService.findEvents() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new EventsError({ + name: 'GET_EVENTS_ERROR', + message: 'EventsService.findEvents() call failed', + cause: error, + }), + this.logger, + ); } } @@ -50,13 +51,14 @@ export class EventsService { try { return await this.prisma.events.count(); } catch (error) { - throwTypedError(new EventsError( - { - name: "GET_EVENTS_COUNT_ERROR", - message: "EventsService.getEventsCount() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new EventsError({ + name: 'GET_EVENTS_COUNT_ERROR', + message: 'EventsService.getEventsCount() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/field-mapping/field-mapping.service.ts b/packages/api/src/@core/field-mapping/field-mapping.service.ts index 08d8c99ac..6fd35e53e 100644 --- a/packages/api/src/@core/field-mapping/field-mapping.service.ts +++ b/packages/api/src/@core/field-mapping/field-mapping.service.ts @@ -8,7 +8,12 @@ import { } from './dto/create-custom-field.dto'; import { v4 as uuidv4 } from 'uuid'; import axios from 'axios'; -import { ActionType, CustomFieldsError, handleServiceError, throwTypedError } from '@@core/utils/errors'; +import { + ActionType, + CustomFieldsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { CrmObject } from '@crm/@lib/@types'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { CONNECTORS_METADATA } from '@panora/shared'; @@ -31,13 +36,14 @@ export class FieldMappingService { }, }); } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "GET_ATTRIBUTES_ERROR", - message: "FieldMappingService.getAttributes() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'GET_ATTRIBUTES_ERROR', + message: 'FieldMappingService.getAttributes() call failed', + cause: error, + }), + this.logger, + ); } } @@ -45,13 +51,14 @@ export class FieldMappingService { try { return await this.prisma.value.findMany(); } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "GET_VALUES_ERROR", - message: "FieldMappingService.getValues() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'GET_VALUES_ERROR', + message: 'FieldMappingService.getValues() call failed', + cause: error, + }), + this.logger, + ); } } @@ -59,13 +66,14 @@ export class FieldMappingService { try { return await this.prisma.entity.findMany(); } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "GET_ENTITIES_ERROR", - message: "FieldMappingService.getEntities() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'GET_ENTITIES_ERROR', + message: 'FieldMappingService.getEntities() call failed', + cause: error, + }), + this.logger, + ); } } @@ -87,13 +95,14 @@ export class FieldMappingService { }, }); } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "GET_CUSTOM_FIELDS_ERROR", - message: "FieldMappingService.getCustomFieldMappings() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'GET_CUSTOM_FIELDS_ERROR', + message: 'FieldMappingService.getCustomFieldMappings() call failed', + cause: error, + }), + this.logger, + ); } } @@ -121,13 +130,14 @@ export class FieldMappingService { return attribute; } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "CREATE_DEFINE_FIELD_ERROR", - message: "FieldMappingService.defineTargetField() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_DEFINE_FIELD_ERROR', + message: 'FieldMappingService.defineTargetField() call failed', + cause: error, + }), + this.logger, + ); } } @@ -147,13 +157,14 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "CREATE_MAP_FIELD_ERROR", - message: "FieldMappingService.mapFieldToProvider() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_MAP_FIELD_ERROR', + message: 'FieldMappingService.mapFieldToProvider() call failed', + cause: error, + }), + this.logger, + ); } } @@ -189,13 +200,14 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - throwTypedError(new CustomFieldsError( - { - name: "CREATE_CUSTOM_FIELD_ERROR", - message: "FieldMappingService.createCustomField() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_CUSTOM_FIELD_ERROR', + message: 'FieldMappingService.createCustomField() call failed', + cause: error, + }), + this.logger, + ); } } @@ -238,12 +250,17 @@ export class FieldMappingService { statusCode: 200, }; } catch (error) { - handleServiceError( - error, + throwTypedError( + new CustomFieldsError({ + name: 'GET_3RD_PARTY_REMOTE_PROPERTIES', + message: `FieldMappingService.getCustomProperties() call failed ---> ${format3rdPartyError( + providerId, + CrmObject.contact, + ActionType.GET, + )}`, + cause: error, + }), this.logger, - providerId, - CrmObject.contact, - ActionType.GET, ); } } diff --git a/packages/api/src/@core/linked-users/linked-users.service.ts b/packages/api/src/@core/linked-users/linked-users.service.ts index 59a485b76..ebbbab642 100644 --- a/packages/api/src/@core/linked-users/linked-users.service.ts +++ b/packages/api/src/@core/linked-users/linked-users.service.ts @@ -6,7 +6,7 @@ import { import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError, LinkedUserError, throwTypedError } from '@@core/utils/errors'; +import { LinkedUserError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class LinkedUsersService { @@ -21,13 +21,14 @@ export class LinkedUsersService { }, }); } catch (error) { - throwTypedError(new LinkedUserError( - { - name: "GET_LINKED_USERS_ERROR", - message: "LinkedUsersService.getLinkedUsers() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USERS_ERROR', + message: 'LinkedUsersService.getLinkedUsers() call failed', + cause: error, + }), + this.logger, + ); } } async getLinkedUser(id: string) { @@ -38,13 +39,14 @@ export class LinkedUsersService { }, }); } catch (error) { - throwTypedError(new LinkedUserError( - { - name: "GET_LINKED_USER_ERROR", - message: "LinkedUsersService.getLinkedUser() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USER_ERROR', + message: 'LinkedUsersService.getLinkedUser() call failed', + cause: error, + }), + this.logger, + ); } } async getLinkedUserV2(originId: string) { @@ -55,13 +57,14 @@ export class LinkedUsersService { }, }); } catch (error) { - throwTypedError(new LinkedUserError( - { - name: "GET_LINKED_USER_FROM_REMOTE_ID_ERROR", - message: "LinkedUsersService.getLinkedUserV2() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USER_FROM_REMOTE_ID_ERROR', + message: 'LinkedUsersService.getLinkedUserV2() call failed', + cause: error, + }), + this.logger, + ); } } async addLinkedUser(data: CreateLinkedUserDto) { @@ -76,13 +79,14 @@ export class LinkedUsersService { }); return res; } catch (error) { - throwTypedError(new LinkedUserError( - { - name: "CREATE_LINKED_USER_ERROR", - message: "LinkedUsersService.addlinkedUser() call failed", - cause: error - } - ), this.logger) ; + throwTypedError( + new LinkedUserError({ + name: 'CREATE_LINKED_USER_ERROR', + message: 'LinkedUsersService.addlinkedUser() call failed', + cause: error, + }), + this.logger, + ); } } async addBatchLinkedUsers(data: CreateBatchLinkedUserDto) { @@ -103,13 +107,14 @@ export class LinkedUsersService { return res; } catch (error) { - throwTypedError(new LinkedUserError( - { - name: "CREATE_BATCH_LINKED_USER_ERROR", - message: "LinkedUsersService.addBatchLinkedUsers() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new LinkedUserError({ + name: 'CREATE_BATCH_LINKED_USER_ERROR', + message: 'LinkedUsersService.addBatchLinkedUsers() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts index aa1e9c10e..e163877dc 100644 --- a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts +++ b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts @@ -28,13 +28,14 @@ export class ManagedWebhooksService { }, }); } catch (error) { - throwTypedError(new ManagedWebhooksError( - { - name: "GET_MANAGED_WEBHOOKS_ERROR", - message: "ManagedWebhooksService.getManagedWebhook() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ManagedWebhooksError({ + name: 'GET_MANAGED_WEBHOOKS_ERROR', + message: 'ManagedWebhooksService.getManagedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -45,13 +46,15 @@ export class ManagedWebhooksService { data: { active: active }, }); } catch (error) { - throwTypedError(new ManagedWebhooksError( - { - name: "UPDATE_MANAGED_WEBHOOK_STATUS_ERROR", - message: "ManagedWebhooksService.updateStatusManagedWebhookEndpoint() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ManagedWebhooksError({ + name: 'UPDATE_MANAGED_WEBHOOK_STATUS_ERROR', + message: + 'ManagedWebhooksService.updateStatusManagedWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } @@ -71,13 +74,14 @@ export class ManagedWebhooksService { }, }); } catch (error) { - throwTypedError(new ManagedWebhooksError( - { - name: "CREATE_MANAGED_WEBHOOK_ERROR", - message: "ManagedWebhooksService.createManagedWebhook() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ManagedWebhooksError({ + name: 'CREATE_MANAGED_WEBHOOK_ERROR', + message: 'ManagedWebhooksService.createManagedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -88,7 +92,7 @@ export class ManagedWebhooksService { id_connection: data.id_connection, }, }); - if(!conn) throw new ReferenceError('Connection undefined') + if (!conn) throw new ReferenceError('Connection undefined'); switch (conn.vertical) { case ConnectorCategory.Ticketing: return await this.ticketingHandler.createExternalWebhook( @@ -98,13 +102,15 @@ export class ManagedWebhooksService { ); } } catch (error) { - throwTypedError(new ManagedWebhooksError( - { - name: "CREATE_REMOTE_WEBHOOK_ERROR", - message: "ManagedWebhooksService.createRemoteThirdPartyWebhook() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ManagedWebhooksError({ + name: 'CREATE_REMOTE_WEBHOOK_ERROR', + message: + 'ManagedWebhooksService.createRemoteThirdPartyWebhook() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/organisations/organisations.service.ts b/packages/api/src/@core/organisations/organisations.service.ts index fc47290de..c1f84aef3 100644 --- a/packages/api/src/@core/organisations/organisations.service.ts +++ b/packages/api/src/@core/organisations/organisations.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { CreateOrganizationDto } from './dto/create-organization.dto'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; @Injectable() export class OrganisationsService { @@ -15,7 +14,6 @@ export class OrganisationsService { try { return await this.prisma.organizations.findMany(); } catch (error) { - handleServiceError(error, this.logger); } } async createOrganization(data: CreateOrganizationDto) { @@ -28,7 +26,6 @@ export class OrganisationsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); } }*/ } diff --git a/packages/api/src/@core/passthrough/passthrough.service.ts b/packages/api/src/@core/passthrough/passthrough.service.ts index 403ae710f..1f33fc793 100644 --- a/packages/api/src/@core/passthrough/passthrough.service.ts +++ b/packages/api/src/@core/passthrough/passthrough.service.ts @@ -5,9 +5,9 @@ import axios, { AxiosResponse } from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { CONNECTORS_METADATA } from '@panora/shared'; +import { PassthroughRequestError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class PassthroughService { @@ -93,7 +93,14 @@ export class PassthroughService { data: response.data, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new PassthroughRequestError({ + name: 'PASSTHROUGH_REMOTE_API_CALL_ERROR', + message: 'WebhookService.sendPassthroughRequest() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/project-connectors/project-connectors.service.ts b/packages/api/src/@core/project-connectors/project-connectors.service.ts index 93510cfa7..35236c503 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.service.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.service.ts @@ -1,8 +1,8 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { ConnectorSetError, handleServiceError, throwTypedError } from '@@core/utils/errors'; +import { ConnectorSetError, throwTypedError } from '@@core/utils/errors'; import { TypeCustom } from './project-connectors.controller'; @Injectable() @@ -52,13 +52,15 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - throwTypedError(new ConnectorSetError( - { - name: "UPDATE_CONNECTOR_SET_ERROR", - message: "ProjectConnectorsService.updateProjectConnectors() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectorSetError({ + name: 'UPDATE_CONNECTOR_SET_ERROR', + message: + 'ProjectConnectorsService.updateProjectConnectors() call failed', + cause: error, + }), + this.logger, + ); } } @@ -84,13 +86,15 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - throwTypedError(new ConnectorSetError( - { - name: "CREATE_CONNECTOR_SET_ERROR", - message: "ProjectConnectorsService.createProjectConnectors() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectorSetError({ + name: 'CREATE_CONNECTOR_SET_ERROR', + message: + 'ProjectConnectorsService.createProjectConnectors() call failed', + cause: error, + }), + this.logger, + ); } } @@ -112,19 +116,19 @@ export class ProjectConnectorsService { }, }); if (!res) { - throw new ReferenceError( - 'Connector set undefined!', - ); + throw new ReferenceError('Connector set undefined!'); } return res; } catch (error) { - throwTypedError(new ConnectorSetError( - { - name: "GET_CONNECTOR_SET_BY_PROJECT_ERROR", - message: "ProjectConnectorsService.getConnectorsbyProjectId() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectorSetError({ + name: 'GET_CONNECTOR_SET_BY_PROJECT_ERROR', + message: + 'ProjectConnectorsService.getConnectorsbyProjectId() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/sync/sync.service.ts b/packages/api/src/@core/sync/sync.service.ts index f4d041699..56fe87899 100644 --- a/packages/api/src/@core/sync/sync.service.ts +++ b/packages/api/src/@core/sync/sync.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@nestjs/common'; import { LoggerService } from '../logger/logger.service'; import { ConnectorCategory } from '@panora/shared'; -import { CrmObject, ENGAGEMENTS_TYPE } from '@crm/@lib/@types'; +import { ENGAGEMENTS_TYPE } from '@crm/@lib/@types'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { handleServiceError } from '@@core/utils/errors'; import { SyncService as CrmCompanySyncService } from '@crm/company/sync/sync.service'; import { SyncService as CrmContactSyncService } from '@crm/contact/sync/sync.service'; import { SyncService as CrmDealSyncService } from '@crm/deal/sync/sync.service'; @@ -20,6 +19,7 @@ import { SyncService as TicketingTagSyncService } from '@ticketing/tag/sync/sync import { SyncService as TicketingTeamSyncService } from '@ticketing/team/sync/sync.service'; import { SyncService as TicketingTicketSyncService } from '@ticketing/ticket/sync/sync.service'; import { SyncService as TicketingUserSyncService } from '@ticketing/user/sync/sync.service'; +import { throwTypedError, CoreSyncError } from '@@core/utils/errors'; @Injectable() export class CoreSyncService { @@ -51,32 +51,87 @@ export class CoreSyncService { vertical: string, provider: string, linkedUserId: string, - id_project: string + id_project: string, ) { try { - const tasks = []; switch (vertical) { case ConnectorCategory.Crm: - // logic - tasks.push(() => this.CrmUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmCompanySyncService.syncCompaniesForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmDealSyncService.syncDealsForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.CrmUserSyncService.syncUsersForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmCompanySyncService.syncCompaniesForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmContactSyncService.syncContactsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmDealSyncService.syncDealsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const type of ENGAGEMENTS_TYPE) { - tasks.push(() => this.CrmEngagementSyncService.syncEngagementsForLinkedUser(provider, linkedUserId, id_project, type)); + tasks.push(() => + this.CrmEngagementSyncService.syncEngagementsForLinkedUser( + provider, + linkedUserId, + id_project, + type, + ), + ); } - tasks.push(() => this.CrmNoteSyncService.syncNotesForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmTaskSyncService.syncTasksForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.CrmNoteSyncService.syncNotesForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmTaskSyncService.syncTasksForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const task of tasks) { try { await task(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'CRM_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -98,18 +153,68 @@ export class CoreSyncService { case ConnectorCategory.Ticketing: // logic - tasks.push(() => this.TicketingUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingAccountSyncService.syncAccountsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingCollectionSyncService.syncCollectionsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingTicketSyncService.syncTicketsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingTeamSyncService.syncTeamsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.TicketingUserSyncService.syncUsersForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingAccountSyncService.syncAccountsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingCollectionSyncService.syncCollectionsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingTicketSyncService.syncTicketsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingTeamSyncService.syncTeamsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingContactSyncService.syncContactsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const task of tasks) { try { await task(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'TICKETING_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -122,27 +227,70 @@ export class CoreSyncService { for (const ticket of tickets) { try { - await this.TicketingCommentSyncService.syncCommentsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket); - await this.TicketingTagSyncService.syncTagsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket); + await this.TicketingCommentSyncService.syncCommentsForLinkedUser( + provider, + linkedUserId, + id_project, + ticket.id_tcg_ticket, + ); + await this.TicketingTagSyncService.syncTagsForLinkedUser( + provider, + linkedUserId, + id_project, + ticket.id_tcg_ticket, + ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'TICKETING_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } break; } - } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } // we must have a sync_jobs table with 7 (verticals) rows, one of each is syncing details async getSyncStatus(vertical: string) { try { - } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'GET_SYNC_STATUS_ERROR', + message: 'CoreSyncService.getSyncStatus() call failed', + cause: error, + }), + this.logger, + ); } } @@ -182,7 +330,14 @@ export class CoreSyncService { status: `SYNCING`, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'RESYNC_ERROR', + message: 'CoreSyncService.resync() call failed', + cause: error, + }), + this.logger, + ); } finally { // Handle background tasks completion Promise.allSettled(tasks).then((results) => { diff --git a/packages/api/src/@core/utils/errors.ts b/packages/api/src/@core/utils/errors.ts index 94f388339..1b3763070 100644 --- a/packages/api/src/@core/utils/errors.ts +++ b/packages/api/src/@core/utils/errors.ts @@ -5,7 +5,10 @@ import { Prisma } from '@prisma/client'; import { TargetObject } from './types'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -type TypedError = AxiosError | PrismaClientKnownRequestError | T; +type TypedError = + | AxiosError + | PrismaClientKnownRequestError + | T; export class ErrorBase extends Error { name: T; @@ -13,9 +16,9 @@ export class ErrorBase extends Error { cause: any; constructor({ - name, + name, message, - cause + cause, }: { name: T; message: string; @@ -31,6 +34,7 @@ export class ErrorBase extends Error { export enum Action { oauthCallback = 'oauth-callback', oauthRefresh = 'oauth-refresh', + webhookCreation = 'webhook-creation', } export enum ActionType { @@ -41,200 +45,224 @@ export enum ActionType { } export class ProjectError extends ErrorBase< - "CREATE_PROJECT_ERROR" | - "GET_PROJECT_FOR_USER_ERROR" | - "GET_PROJECTS_ERROR" ->{} + 'CREATE_PROJECT_ERROR' | 'GET_PROJECT_FOR_USER_ERROR' | 'GET_PROJECTS_ERROR' +> {} export class ConnectorSetError extends ErrorBase< - "CREATE_CONNECTOR_SET_ERROR" | - "GET_CONNECTOR_SET_BY_PROJECT_ERROR" | - "UPDATE_CONNECTOR_SET_ERROR" ->{} + | 'CREATE_CONNECTOR_SET_ERROR' + | 'GET_CONNECTOR_SET_BY_PROJECT_ERROR' + | 'UPDATE_CONNECTOR_SET_ERROR' +> {} export class LinkedUserError extends ErrorBase< - "CREATE_LINKED_USER_ERROR" | - "CREATE_BATCH_LINKED_USER_ERROR" | - "GET_LINKED_USER_FROM_REMOTE_ID_ERROR" | - "GET_LINKED_USERS_ERROR" | - "GET_LINKED_USER_ERROR" ->{} + | 'CREATE_LINKED_USER_ERROR' + | 'CREATE_BATCH_LINKED_USER_ERROR' + | 'GET_LINKED_USER_FROM_REMOTE_ID_ERROR' + | 'GET_LINKED_USERS_ERROR' + | 'GET_LINKED_USER_ERROR' +> {} export class ConnectionStrategiesError extends ErrorBase< - "CREATE_CONNECTION_STRATEGY_ERROR" | - "DELETE_CONNECTION_STRATEGY_ERROR" | - "UPDATE_CONNECTION_STRATEGY_ERROR" | - "GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR" | - "GET_CONNECTION_STRATEGY_STATUS_ERROR" | - "GET_CREDENTIALS_ERROR" | - "TOGGLE_CONNECTION_STRATEGY_ERROR" | - "CONNECTION_STRATEGY_ALREADY_EXISTS" | - "CUSTOM_CREDENTIALS_ERROR" ->{} + | 'CREATE_CONNECTION_STRATEGY_ERROR' + | 'DELETE_CONNECTION_STRATEGY_ERROR' + | 'UPDATE_CONNECTION_STRATEGY_ERROR' + | 'GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR' + | 'GET_CONNECTION_STRATEGY_STATUS_ERROR' + | 'GET_CREDENTIALS_ERROR' + | 'TOGGLE_CONNECTION_STRATEGY_ERROR' + | 'CONNECTION_STRATEGY_ALREADY_EXISTS' + | 'CUSTOM_CREDENTIALS_ERROR' +> {} export class EncryptionError extends ErrorBase< - "ENCRYPT_ERROR" | - "DECRYPT_ERROR" ->{} + 'ENCRYPT_ERROR' | 'DECRYPT_ERROR' +> {} export class EventsError extends ErrorBase< - "GET_EVENTS_ERROR" | - "GET_EVENTS_COUNT_ERROR" ->{} + 'GET_EVENTS_ERROR' | 'GET_EVENTS_COUNT_ERROR' +> {} export class MagicLinksError extends ErrorBase< - "CREATE_MAGIC_LINK_ERROR" | - "GET_MAGIC_LINKS_ERROR" | - "GET_MAGIC_LINK_ERROR" ->{} + 'CREATE_MAGIC_LINK_ERROR' | 'GET_MAGIC_LINKS_ERROR' | 'GET_MAGIC_LINK_ERROR' +> {} export class CustomFieldsError extends ErrorBase< - "CREATE_DEFINE_FIELD_ERROR" | - "CREATE_MAP_FIELD_ERROR" | - "CREATE_CUSTOM_FIELD_ERROR" | - "GET_CUSTOM_FIELDS_ERROR" | - "GET_ENTITIES_ERROR" | - "GET_ATTRIBUTES_ERROR" | - "GET_VALUES_ERROR" ->{} + | 'GET_3RD_PARTY_REMOTE_PROPERTIES' + | 'CREATE_DEFINE_FIELD_ERROR' + | 'CREATE_MAP_FIELD_ERROR' + | 'CREATE_CUSTOM_FIELD_ERROR' + | 'GET_CUSTOM_FIELDS_ERROR' + | 'GET_ENTITIES_ERROR' + | 'GET_ATTRIBUTES_ERROR' + | 'GET_VALUES_ERROR' +> {} export class ConnectionsError extends ErrorBase< - "GET_CONNECTIONS_ERROR" | - "OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR" | - "OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR" | - "OAUTH_CALLBACK_ERROR" | - "HANDLE_OAUTH_CALLBACK_TICKETING" | - "HANDLE_OAUTH_CALLBACK_CRM" | - "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION" | - "HANDLE_OAUTH_CALLBACK_FILESTORAGE" | - "HANDLE_OAUTH_CALLBACK_ACCOUNTING" | - "HANDLE_OAUTH_CALLBACK_ATS" | - "HANDLE_OAUTH_CALLBACK_HRIS" | - "HANDLE_OAUTH_REFRESH_TICKETING" | - "HANDLE_OAUTH_REFRESH_CRM" | - "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION" | - "HANDLE_OAUTH_REFRESH_FILESTORAGE" | - "HANDLE_OAUTH_REFRESH_ACCOUNTING" | - "HANDLE_OAUTH_REFRESH_ATS" | - "HANDLE_OAUTH_REFRESH_HRIS" ->{} + | 'GET_CONNECTION_FROM_CONNECTION_TOKEN_ERROR' + | 'GET_CONNECTIONS_ERROR' + | 'OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR' + | 'OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR' + | 'OAUTH_CALLBACK_ERROR' + | 'HANDLE_OAUTH_CALLBACK_TICKETING' + | 'HANDLE_OAUTH_CALLBACK_CRM' + | 'HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION' + | 'HANDLE_OAUTH_CALLBACK_FILESTORAGE' + | 'HANDLE_OAUTH_CALLBACK_ACCOUNTING' + | 'HANDLE_OAUTH_CALLBACK_ATS' + | 'HANDLE_OAUTH_CALLBACK_HRIS' + | 'HANDLE_OAUTH_REFRESH_TICKETING' + | 'HANDLE_OAUTH_REFRESH_CRM' + | 'HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION' + | 'HANDLE_OAUTH_REFRESH_FILESTORAGE' + | 'HANDLE_OAUTH_REFRESH_ACCOUNTING' + | 'HANDLE_OAUTH_REFRESH_ATS' + | 'HANDLE_OAUTH_REFRESH_HRIS' +> {} export class ManagedWebhooksError extends ErrorBase< - "GET_MANAGED_WEBHOOKS_ERROR" | - "CREATE_MANAGED_WEBHOOK_ERROR" | - "UPDATE_MANAGED_WEBHOOK_STATUS_ERROR" | - "CREATE_REMOTE_WEBHOOK_ERROR" ->{} + | 'GET_MANAGED_WEBHOOKS_ERROR' + | 'CREATE_MANAGED_WEBHOOK_ERROR' + | 'UPDATE_MANAGED_WEBHOOK_STATUS_ERROR' + | 'CREATE_REMOTE_WEBHOOK_ERROR' + | 'SIGNATURE_VERIFICATION_AUTHENTICITY_ERROR' + | 'RECEIVING_WEBHOOK_PAYLOAD_ERROR' +> {} export class AuthError extends ErrorBase< - "GET_API_KEYS_ERROR" | - "GET_USERS_ERROR" | - "VERIFY_USER_ERROR" | - "LOGIN_USER_ERROR"| - "REGISTER_USER_ERROR" | - "CREATE_USER_ERROR" | - "DELETE_API_KEY_ERROR" | - "REFRESH_ACCESS_TOKEN_ERROR" | - "HASH_API_KEY_ERROR" | - "GENERATE_API_KEY_ERROR" | - "VALIDATE_API_KEY_ERROR" | - "EMAIL_ALREADY_EXISTS_ERROR" ->{} - -export class PassthroughRequestError extends ErrorBase< - "PASSTHROUGH_REMOTE_API_CALL_ERROR" ->{} + | 'GET_API_KEYS_ERROR' + | 'GET_USERS_ERROR' + | 'VERIFY_USER_ERROR' + | 'LOGIN_USER_ERROR' + | 'REGISTER_USER_ERROR' + | 'CREATE_USER_ERROR' + | 'DELETE_API_KEY_ERROR' + | 'REFRESH_ACCESS_TOKEN_ERROR' + | 'HASH_API_KEY_ERROR' + | 'GENERATE_API_KEY_ERROR' + | 'VALIDATE_API_KEY_ERROR' + | 'EMAIL_ALREADY_EXISTS_ERROR' +> {} + +export class PassthroughRequestError extends ErrorBase<'PASSTHROUGH_REMOTE_API_CALL_ERROR'> {} export class WebhooksError extends ErrorBase< - "CREATE_WEBHOOK_ERROR" | - "DELETE_WEBHOOK_ERROR" | - "UPDATE_WEBHOOK_STATUS_ERROR" | - "VERIFY_PAYLOAD_ERROR" | - "GET_WEBHOOKS_ERROR" | - "INVALID_SIGNATURE_ERROR" | - "SIGNATURE_GENERATION_ERROR" ->{} + | 'CREATE_WEBHOOK_ERROR' + | 'DELETE_WEBHOOK_ERROR' + | 'UPDATE_WEBHOOK_STATUS_ERROR' + | 'VERIFY_PAYLOAD_ERROR' + | 'GET_WEBHOOKS_ERROR' + | 'INVALID_SIGNATURE_ERROR' + | 'SIGNATURE_GENERATION_ERROR' + | 'DELIVERING_WEBHOOK_ERROR' + | 'DELIVERING_FAILED_WEBHOOK_ERROR' + | 'DELIVERING_PRIORITY_WEBHOOK_ERROR' +> {} export class SyncError extends ErrorBase< - "TICKETING_USER_SYNC_ERROR" | - "TICKETING_TICKET_SYNC_ERROR" | - "TICKETING_ACCOUNT_SYNC_ERROR" | - "TICKETING_CONTACT_SYNC_ERROR" | - "TICKETING_TEAM_SYNC_ERROR" | - "TICKETING_TAG_SYNC_ERROR" | - "TICKETING_COMMENT_SYNC_ERROR" | - "TICKETING_ATTACHMENT_SYNC_ERROR" | - "TICKETING_COLLECTION_SYNC_ERROR" | - "CRM_COMPANY_SYNC_ERROR" | - "CRM_CONTACT_SYNC_ERROR" | - "CRM_DEAL_SYNC_ERROR" | - "CRM_ENGAGEMENT_SYNC_ERROR" | - "CRM_NOTE_SYNC_ERROR" | - "CRM_STAGE_SYNC_ERROR" | - "CRM_TASK_SYNC_ERROR" | - "CRM_USER_SYNC_ERROR" ->{} - -/* */ -// Custom error for general application errors -export class AppError extends Error { - constructor(message: string) { - super(message); - this.name = 'AppError'; - } -} - -// Custom error for not found (404) errors -export class NotFoundError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.NOT_FOUND); - this.name = 'NotFoundError'; - } -} - -// Custom error for bad request (400) errors -export class BadRequestError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.BAD_REQUEST); - this.name = 'BadRequestError'; - } -} - -// Custom error for unauthorized (401) errors -export class UnauthorizedError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.UNAUTHORIZED); - this.name = 'UnauthorizedError'; - } -} - -// Custom error for duplicate element inside Prisma DB errors -export class NotUniqueRecord extends Error { - constructor(message: string) { - super(message); - this.name = 'NotUniqueRecord'; - } -} - -export class ThirdPartyApiCallError extends Error { - constructor(message: string) { + | 'TICKETING_USER_SYNC_ERROR' + | 'TICKETING_TICKET_SYNC_ERROR' + | 'TICKETING_ACCOUNT_SYNC_ERROR' + | 'TICKETING_CONTACT_SYNC_ERROR' + | 'TICKETING_TEAM_SYNC_ERROR' + | 'TICKETING_TAG_SYNC_ERROR' + | 'TICKETING_COMMENT_SYNC_ERROR' + | 'TICKETING_ATTACHMENT_SYNC_ERROR' + | 'TICKETING_COLLECTION_SYNC_ERROR' + | 'CRM_COMPANY_SYNC_ERROR' + | 'CRM_CONTACT_SYNC_ERROR' + | 'CRM_DEAL_SYNC_ERROR' + | 'CRM_ENGAGEMENT_SYNC_ERROR' + | 'CRM_NOTE_SYNC_ERROR' + | 'CRM_STAGE_SYNC_ERROR' + | 'CRM_TASK_SYNC_ERROR' + | 'CRM_USER_SYNC_ERROR' +> {} + +export class CoreSyncError extends ErrorBase< + | 'INITIAL_SYNC_ERROR' + | 'GET_SYNC_STATUS_ERROR' + | 'RESYNC_ERROR' + | 'CRM_INITIAL_SYNC_ERROR' + | 'TICKETING_INITIAL_SYNC_ERROR' + | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' + | 'FILESTORAGE_INITIAL_SYNC_ERROR' + | 'ACCOUNTING_INITIAL_SYNC_ERROR' + | 'HRIS_INITIAL_SYNC_ERROR' + | 'ATS_INITIAL_SYNC_ERROR' + | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' +> {} + +export class UnifiedTicketingError extends ErrorBase< + | 'GET_USER_ERROR' + | 'GET_USERS_ERROR' + | 'GET_TICKET_ERROR' + | 'GET_TICKETS_ERROR' + | 'CREATE_TICKET_ERROR' + | 'CREATE_TICKETS_ERROR' + | 'GET_TEAM_ERROR' + | 'GET_TEAMS_ERROR' + | 'GET_TAG_ERROR' + | 'GET_TAGS_ERROR' + | 'GET_CONTACT_ERROR' + | 'GET_CONTACTS_ERROR' + | 'GET_ACCOUNTS_ERROR' + | 'GET_ACCOUNT_ERROR' + | 'GET_COMMENT_ERROR' + | 'GET_COMMENTS_ERROR' + | 'CREATE_COMMENTS_ERROR' + | 'CREATE_COMMENT_ERROR' + | 'GET_ATTACHMENTS_ERROR' + | 'GET_ATTACHMENT_ERROR' + | 'CREATE_ATTACHMENTS_ERROR' + | 'CREATE_ATTACHMENT_ERROR' + | 'GET_COLLECTION_ERROR' + | 'GET_COLLECTIONS_ERROR' +> {} + +export class UnifiedCrmError extends ErrorBase< + | 'GET_COMPANY_ERROR' + | 'GET_COMPANIES_ERROR' + | 'CREATE_COMPANY_ERROR' + | 'CREATE_COMPANIES_ERROR' + | 'GET_CONTACTS_ERROR' + | 'GET_CONTACT_ERROR' + | 'CREATE_CONTACT_ERROR' + | 'CREATE_CONTACTS_ERROR' + | 'GET_DEAL_ERROR' + | 'GET_DEALS_ERROR' + | 'CREATE_DEAL_ERROR' + | 'CREATE_DEALS_ERROR' + | 'GET_ENGAGEMENT_ERROR' + | 'GET_ENGAGEMENTS_ERROR' + | 'CREATE_ENGAGEMENTS_ERROR' + | 'CREATE_ENGAGEMENT_ERROR' + | 'GET_NOTES_ERROR' + | 'GET_NOTE_ERROR' + | 'CREATE_NOTES_ERROR' + | 'CREATE_NOTE_ERROR' + | 'GET_STAGE_ERROR' + | 'GET_STAGES_ERROR' + | 'GET_TASKS_ERROR' + | 'GET_TASK_ERROR' + | 'CREATE_TASKS_ERROR' + | 'CREATE_TASK_ERROR' + | 'GET_USERS_ERROR' + | 'GET_USER_ERROR' +> {} + +export class ThirdPartyApiServiceError extends Error { + cause: any; + constructor(message: string, cause?: any) { super(message); - this.name = 'ThirdPartyApiCallError'; + this.name = 'ThirdPartyApiServiceError'; + this.cause = cause; } } -export class PrismaError extends Error { - constructor(message: string) { - super(message); - this.name = 'PrismaError'; +export function throwTypedError(error: TypedError, logger?: LoggerService) { + const errorMessage = error.message; + if (logger) { + logger.error('Error message: ', errorMessage); } -} - -export function throwTypedError( - error: TypedError, - logger: LoggerService, -) { - let errorMessage = error.message; - logger.error('Error message: ', errorMessage); throw error; } @@ -242,57 +270,31 @@ export function format3rdPartyError( providerName: string, action: TargetObject | Action, actionType: ActionType, -){ - switch(actionType) { +) { + switch (actionType) { case 'POST': - return `Failed to create ${action} for ${providerName}` + return `Failed to create ${action} for ${providerName}`; case 'GET': - return `Failed to retrieve ${action} for ${providerName}` + return `Failed to retrieve ${action} for ${providerName}`; case 'PUT': - return `Failed to update ${action} for ${providerName}` + return `Failed to update ${action} for ${providerName}`; case 'DELETE': - return `Failed to delete ${action} for ${providerName}` + return `Failed to delete ${action} for ${providerName}`; } } - -export function handleServiceError( - error: TypedError, +export function handle3rdPartyServiceError( + error: any, logger: LoggerService, - providerName?: string, - action?: TargetObject | Action, - actionType?: ActionType, + providerName: string, + action: TargetObject | Action, + actionType: ActionType, ) { - let statusCode = 500; // Default to internal server error - let errorMessage = error.message; - - if (axios.isAxiosError(error)) { - statusCode = error.response?.status || 500; // Use HTTP status code from Axios error or default to 500 - errorMessage = error.response?.data || error.message; - logger.error('Error with Axios request:', errorMessage); - } - - if (error instanceof Prisma.PrismaClientKnownRequestError) { - logger.error('Error with Prisma request:', errorMessage); - throw new PrismaError(errorMessage); - } - - if (error instanceof HttpException) { - throw error; - } - - const message = - action && providerName && actionType - ? actionType == 'POST' - ? `Failed to create ${action} for ${providerName} : ${errorMessage}` - : actionType == 'GET' - ? `Failed to retrieve ${action} for ${providerName} : ${errorMessage}` - : actionType == 'PUT' - ? `Failed to update ${action} for ${providerName} : ${errorMessage}` - : `Failed to delete ${action} for ${providerName} : ${errorMessage}` - : errorMessage; - - throw new ThirdPartyApiCallError( - message - ) + throwTypedError( + new ThirdPartyApiServiceError( + format3rdPartyError(providerName, action, actionType), + error, + ), + logger, + ); } diff --git a/packages/api/src/@core/utils/services/validateUser.service.ts b/packages/api/src/@core/utils/services/validateUser.service.ts index 47b65ac8c..b439fbd90 100644 --- a/packages/api/src/@core/utils/services/validateUser.service.ts +++ b/packages/api/src/@core/utils/services/validateUser.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; @Injectable() export class ValidateUserService { @@ -15,11 +14,11 @@ export class ValidateUserService { }, }); if (project.id_user !== user_id) { - throw new Error('Unauthorized call from sender'); + throw new ReferenceError('User mismatch'); } return true; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/@core/utils/unification/unify.ts b/packages/api/src/@core/utils/unification/unify.ts index 5c651fde0..5eeeb207b 100644 --- a/packages/api/src/@core/utils/unification/unify.ts +++ b/packages/api/src/@core/utils/unification/unify.ts @@ -58,5 +58,4 @@ export async function unify({ customFieldMappings, }); } - return; } diff --git a/packages/api/src/@core/webhook/webhook.service.ts b/packages/api/src/@core/webhook/webhook.service.ts index 4a50fd9bb..44a8b9197 100644 --- a/packages/api/src/@core/webhook/webhook.service.ts +++ b/packages/api/src/@core/webhook/webhook.service.ts @@ -4,7 +4,7 @@ import { InjectQueue } from '@nestjs/bull'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError, throwTypedError, WebhooksError } from '@@core/utils/errors'; +import { throwTypedError, WebhooksError } from '@@core/utils/errors'; import { WebhookDto } from './dto/webhook.dto'; import axios from 'axios'; import crypto from 'crypto'; @@ -20,19 +20,20 @@ export class WebhookService { } generateSignature(payload: any, secret: string): string { - try{ + try { return crypto - .createHmac('sha256', secret) - .update(JSON.stringify(payload)) - .digest('hex'); - }catch(error){ - throwTypedError(new WebhooksError( - { - name: "SIGNATURE_GENERATION_ERROR", - message: "WebhookService.generateSignature() call failed", - cause: error - } - ), this.logger) + .createHmac('sha256', secret) + .update(JSON.stringify(payload)) + .digest('hex'); + } catch (error) { + throwTypedError( + new WebhooksError({ + name: 'SIGNATURE_GENERATION_ERROR', + message: 'WebhookService.generateSignature() call failed', + cause: error, + }), + this.logger, + ); } } @@ -44,13 +45,14 @@ export class WebhookService { }, }); } catch (error) { - throwTypedError(new WebhooksError( - { - name: "GET_WEBHOOKS_ERROR", - message: "WebhookService.getWebhookEndpoints() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new WebhooksError({ + name: 'GET_WEBHOOKS_ERROR', + message: 'WebhookService.getWebhookEndpoints() call failed', + cause: error, + }), + this.logger, + ); } } @@ -61,13 +63,14 @@ export class WebhookService { data: { active: active }, }); } catch (error) { - throwTypedError(new WebhooksError( - { - name: "UPDATE_WEBHOOK_STATUS_ERROR", - message: "WebhookService.updateStatusWebhookEndpoint() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new WebhooksError({ + name: 'UPDATE_WEBHOOK_STATUS_ERROR', + message: 'WebhookService.updateStatusWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } @@ -86,31 +89,33 @@ export class WebhookService { }, }); } catch (error) { - throwTypedError(new WebhooksError( - { - name: "CREATE_WEBHOOK_ERROR", - message: "WebhookService.createWebhookEndpoint() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new WebhooksError({ + name: 'CREATE_WEBHOOK_ERROR', + message: 'WebhookService.createWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } async deleteWebhook(whId: string) { - try{ + try { return await this.prisma.webhook_endpoints.delete({ where: { id_webhook_endpoint: whId, }, }); - }catch(error){ - throwTypedError(new WebhooksError( - { - name: "DELETE_WEBHOOK_ERROR", - message: "WebhookService.deleteWebhook() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new WebhooksError({ + name: 'DELETE_WEBHOOK_ERROR', + message: 'WebhookService.deleteWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -130,14 +135,18 @@ export class WebhookService { active: true, }, }); - if (!webhooks) return; + if (!webhooks) + throw ReferenceError(`Webhook not found for id_project=${projectId}`); const webhook = webhooks.find((wh) => { const scopes = wh.scope; return scopes.includes(eventType); }); - if (!webhook) return; + if (!webhook) + throw ReferenceError( + `Webhook not found for scope eventType=${eventType}`, + ); this.logger.log('handling webhook payload....'); @@ -166,7 +175,14 @@ export class WebhookService { webhook_delivery_id: w_delivery.id_webhook_delivery_attempt, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_WEBHOOK_ERROR', + message: 'WebhookService.handleWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -186,14 +202,20 @@ export class WebhookService { active: true, }, }); - if (!webhooks) return; + if (!webhooks) + throw ReferenceError( + `Webhook endpoint undefined for id_project=${projectId}`, + ); const webhook = webhooks.find((wh) => { const scopes = wh.scope; return scopes.includes(eventType); }); - if (!webhook) return; + if (!webhook) + throw ReferenceError( + `Webhook not found for scope eventType=${eventType}`, + ); this.logger.log('handling webhook payload....'); @@ -290,7 +312,14 @@ export class WebhookService { } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_PRIORITY_WEBHOOK_ERROR', + message: 'WebhookService.handlePriorityWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -303,7 +332,14 @@ export class WebhookService { { delay: 60000 }, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_FAILED_WEBHOOK_ERROR', + message: 'WebhookService.handleFailedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -315,22 +351,21 @@ export class WebhookService { try { const expected = this.generateSignature(payload, secret); if (expected !== signature) { - throw new WebhooksError( - { - name: "INVALID_SIGNATURE_ERROR", - message: `Signature mismatch for the payload received with signature=${signature}`, - } - ) + throw new WebhooksError({ + name: 'INVALID_SIGNATURE_ERROR', + message: `Signature mismatch for the payload received with signature=${signature}`, + }); } return 200; } catch (error) { - throwTypedError(new WebhooksError( - { - name: "VERIFY_PAYLOAD_ERROR", - message: "WebhookService.verifyPayloadSignature() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new WebhooksError({ + name: 'VERIFY_PAYLOAD_ERROR', + message: 'WebhookService.verifyPayloadSignature() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/crm/@lib/@utils/index.ts b/packages/api/src/crm/@lib/@utils/index.ts index b8dfc67eb..cfbf18d30 100644 --- a/packages/api/src/crm/@lib/@utils/index.ts +++ b/packages/api/src/crm/@lib/@utils/index.ts @@ -84,10 +84,10 @@ export class Utils { id_crm_user: uuid, }, }); - if (!res) throw new Error(`crm_user not found for uuid ${uuid}`); + if (!res) throw new ReferenceError(`crm_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -98,10 +98,10 @@ export class Utils { id_crm_user: uuid, }, }); - if (!res) throw new Error(`crm_user not found for uuid ${uuid}`); + if (!res) throw new ReferenceError(`crm_user not found for uuid ${uuid}`); return res; } catch (error) { - throw new Error(error); + throw error; } } @@ -114,13 +114,13 @@ export class Utils { }, }); if (!res) { - throw new Error( + throw new ReferenceError( `crm_user not found for remote_id ${remote_id} and integration ${remote_platform}`, ); } return res.id_crm_user; } catch (error) { - throw new Error(error); + throw error; } } @@ -133,11 +133,12 @@ export class Utils { }, }); - if (!res) return; - + if (!res) { + throw new ReferenceError(`crm_companies not found for id ${id}`); + } return res.name; } catch (error) { - throw new Error(error); + throw error; } } @@ -149,12 +150,12 @@ export class Utils { remote_platform: remote_platform, }, }); - - if (!res) return; + if (!res) + throw new ReferenceError(`crm_deals_stage not found for uuid ${id}`); return res.stage_name; } catch (error) { - throw new Error(error); + throw error; } } @@ -166,11 +167,11 @@ export class Utils { }, }); if (!res) { - throw new Error(`crm_companies not found for uuid ${uuid}`); + throw new ReferenceError(`crm_companies not found for uuid ${uuid}`); } return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -183,13 +184,13 @@ export class Utils { }, }); if (!res) { - throw new Error( + throw new ReferenceError( `crm_companies not found for remote_id ${remote_id} and integration ${remote_platform}`, ); } return res.id_crm_company; } catch (error) { - throw new Error(error); + throw error; } } @@ -200,10 +201,11 @@ export class Utils { id_crm_deals_stage: uuid, }, }); - if (!res) throw new Error(`crm_deals_stages not found for uuid ${uuid}`); + if (!res) + throw new ReferenceError(`crm_deals_stages not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -216,7 +218,7 @@ export class Utils { }, }); if (!res) { - throw new Error( + throw new ReferenceError( `crm_deals_stages not found for remote_id ${remote_id} and integration ${remote_platform}`, ); } @@ -233,10 +235,11 @@ export class Utils { id_crm_contact: uuid, }, }); - if (!res) throw new Error(`crm_contacts not found for uuid ${uuid}`); + if (!res) + throw new ReferenceError(`crm_contacts not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -249,12 +252,12 @@ export class Utils { }, }); if (!res) - throw new Error( + throw new ReferenceError( `crm_contacts not found for remote_id ${remote_id} and integration ${remote_platform}`, ); return res.id_crm_contact; } catch (error) { - throw new Error(error); + throw error; } } @@ -265,10 +268,11 @@ export class Utils { id_crm_deal: uuid, }, }); - if (!res) throw new Error(`crm_deals not found for uuid ${uuid}`); + if (!res) + throw new ReferenceError(`crm_deals not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -281,12 +285,12 @@ export class Utils { }, }); if (!res) - throw new Error( + throw new ReferenceError( `crm_deals not found for remote_id ${remote_id} and integration ${remote_platform}`, ); return res.id_crm_deal; } catch (error) { - throw new Error(error); + throw error; } } @@ -294,12 +298,12 @@ export class Utils { try { switch (provider_name.toLowerCase()) { default: - throw new Error( + throw new ReferenceError( 'Provider not supported for status custom task mapping', ); } } catch (error) { - throw new Error(error); + throw error; } } diff --git a/packages/api/src/crm/company/services/attio/index.ts b/packages/api/src/crm/company/services/attio/index.ts index 23e70299b..c7fdad88e 100644 --- a/packages/api/src/crm/company/services/attio/index.ts +++ b/packages/api/src/crm/company/services/attio/index.ts @@ -4,7 +4,7 @@ import axios from 'axios'; import { CrmObject } from '@crm/@lib/@types'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ICompanyService } from '@crm/company/types'; @@ -58,7 +58,7 @@ export class AttioService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', @@ -97,7 +97,7 @@ export class AttioService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', diff --git a/packages/api/src/crm/company/services/close/index.ts b/packages/api/src/crm/company/services/close/index.ts index 8c87d3591..6a57c529e 100644 --- a/packages/api/src/crm/company/services/close/index.ts +++ b/packages/api/src/crm/company/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -57,7 +57,7 @@ export class CloseService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -105,7 +105,7 @@ export class CloseService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/company/services/company.service.ts b/packages/api/src/crm/company/services/company.service.ts index b86fa027a..f2a7c0999 100644 --- a/packages/api/src/crm/company/services/company.service.ts +++ b/packages/api/src/crm/company/services/company.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCompanyInput, @@ -17,6 +16,7 @@ import { OriginalCompanyOutput } from '@@core/utils/types/original/original.crm' import { unify } from '@@core/utils/unification/unify'; import { ICompanyService } from '../types'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class CompanyService { @@ -52,7 +52,13 @@ export class CompanyService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_COMPANIES_ERROR', + message: 'CompanyService.batchAddCompanies() call failed', + cause: error, + }), + ); } } @@ -70,7 +76,7 @@ export class CompanyService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const user = unifiedCompanyData.user_id; //check if user_id refer to real uuids @@ -81,7 +87,9 @@ export class CompanyService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -363,7 +371,13 @@ export class CompanyService { ); return result_company; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_COMPANY_ERROR', + message: 'CompanyService.addCompany() call failed', + cause: error, + }), + ); } } @@ -447,7 +461,13 @@ export class CompanyService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_COMPANY_ERROR', + message: 'CompanyService.getCompany() call failed', + cause: error, + }), + ); } } @@ -550,7 +570,13 @@ export class CompanyService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_COMPANIES_ERROR', + message: 'CompanyService.getCompanies() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/company/services/hubspot/index.ts b/packages/api/src/crm/company/services/hubspot/index.ts index 8187a6c4e..f15c511be 100644 --- a/packages/api/src/crm/company/services/hubspot/index.ts +++ b/packages/api/src/crm/company/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -108,7 +108,7 @@ export class HubspotService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/company/services/pipedrive/index.ts b/packages/api/src/crm/company/services/pipedrive/index.ts index 60bcc32be..462c40bbd 100644 --- a/packages/api/src/crm/company/services/pipedrive/index.ts +++ b/packages/api/src/crm/company/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -91,7 +91,7 @@ export class PipedriveService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/company/services/zendesk/index.ts b/packages/api/src/crm/company/services/zendesk/index.ts index a8d1ce4c5..11f727c77 100644 --- a/packages/api/src/crm/company/services/zendesk/index.ts +++ b/packages/api/src/crm/company/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements ICompanyService { ActionType.POST, ); } - return; } async syncCompanies( @@ -101,7 +100,7 @@ export class ZendeskService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/company/services/zoho/index.ts b/packages/api/src/crm/company/services/zoho/index.ts index 180c7c52e..e4584a89e 100644 --- a/packages/api/src/crm/company/services/zoho/index.ts +++ b/packages/api/src/crm/company/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements ICompanyService { ActionType.POST, ); } - return; } async syncCompanies( @@ -97,7 +96,7 @@ export class ZohoService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/company/sync/sync.service.ts b/packages/api/src/crm/company/sync/sync.service.ts index baa7de129..bf1eeb2b5 100644 --- a/packages/api/src/crm/company/sync/sync.service.ts +++ b/packages/api/src/crm/company/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -18,6 +17,7 @@ import { CRM_PROVIDERS } from '@panora/shared'; import { Queue } from 'bull'; import { InjectQueue } from '@nestjs/bull'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -39,7 +39,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -106,18 +106,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_COMPANY_SYNC_ERROR', + message: 'SyncService.syncCompanies() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -143,7 +150,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping companies syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -199,7 +208,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -216,7 +225,7 @@ export class SyncService implements OnModuleInit { const originId = company.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingCompany = await this.prisma.crm_companies.findFirst({ @@ -481,7 +490,7 @@ export class SyncService implements OnModuleInit { } return companies_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/contact/services/attio/index.ts b/packages/api/src/crm/contact/services/attio/index.ts index 544a2c461..8a82f9e08 100644 --- a/packages/api/src/crm/contact/services/attio/index.ts +++ b/packages/api/src/crm/contact/services/attio/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -57,7 +57,7 @@ export class AttioService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', @@ -65,7 +65,6 @@ export class AttioService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -104,7 +103,7 @@ export class AttioService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', diff --git a/packages/api/src/crm/contact/services/close/index.ts b/packages/api/src/crm/contact/services/close/index.ts index 579d6bd84..ec6fd24c5 100644 --- a/packages/api/src/crm/contact/services/close/index.ts +++ b/packages/api/src/crm/contact/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class CloseService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -95,7 +95,7 @@ export class CloseService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/contact/services/contact.service.ts b/packages/api/src/crm/contact/services/contact.service.ts index 9bfeaaebe..318593ed2 100644 --- a/packages/api/src/crm/contact/services/contact.service.ts +++ b/packages/api/src/crm/contact/services/contact.service.ts @@ -11,12 +11,12 @@ import { UnifiedContactOutput, } from '@crm/contact/types/model.unified'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { OriginalContactOutput } from '@@core/utils/types/original/original.crm'; import { ServiceRegistry } from './registry.service'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class ContactService { @@ -53,7 +53,13 @@ export class ContactService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_CONTACTS_ERROR', + message: 'ContactService.batchAddContacts() call failed', + cause: error, + }), + ); } } @@ -398,7 +404,13 @@ export class ContactService { ); return result_contact; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_CONTACT_ERROR', + message: 'ContactService.addContact() call failed', + cause: error, + }), + ); } } @@ -479,7 +491,13 @@ export class ContactService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_CONTACT_ERROR', + message: 'ContactService.getContact() call failed', + cause: error, + }), + ); } } @@ -583,7 +601,13 @@ export class ContactService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_CONTACTS_ERROR', + message: 'ContactService.getContacts() call failed', + cause: error, + }), + ); } } //TODO @@ -592,9 +616,7 @@ export class ContactService { updateContactData: Partial, ): Promise { try { - } catch (error) { - handleServiceError(error, this.logger); - } + } catch (error) {} // TODO: fetch the contact from the database using 'id' // TODO: update the contact with 'updateContactData' // TODO: save the updated contact back to the database diff --git a/packages/api/src/crm/contact/services/hubspot/index.ts b/packages/api/src/crm/contact/services/hubspot/index.ts index e083db679..10a36f9d1 100644 --- a/packages/api/src/crm/contact/services/hubspot/index.ts +++ b/packages/api/src/crm/contact/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -61,7 +61,7 @@ export class HubspotService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -110,7 +110,7 @@ export class HubspotService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/contact/services/pipedrive/index.ts b/packages/api/src/crm/contact/services/pipedrive/index.ts index 96301a01f..6e17f6b38 100644 --- a/packages/api/src/crm/contact/services/pipedrive/index.ts +++ b/packages/api/src/crm/contact/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class PipedriveService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -63,7 +63,6 @@ export class PipedriveService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -92,7 +91,7 @@ export class PipedriveService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/contact/services/registry.service.ts b/packages/api/src/crm/contact/services/registry.service.ts index be0d9c608..bc664c41c 100644 --- a/packages/api/src/crm/contact/services/registry.service.ts +++ b/packages/api/src/crm/contact/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IContactService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/crm/contact/services/zendesk/index.ts b/packages/api/src/crm/contact/services/zendesk/index.ts index c5a0c70ec..14bf5bbb9 100644 --- a/packages/api/src/crm/contact/services/zendesk/index.ts +++ b/packages/api/src/crm/contact/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -102,7 +101,7 @@ export class ZendeskService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/contact/services/zoho/index.ts b/packages/api/src/crm/contact/services/zoho/index.ts index e1d3f8a91..a81bb1718 100644 --- a/packages/api/src/crm/contact/services/zoho/index.ts +++ b/packages/api/src/crm/contact/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -98,7 +97,7 @@ export class ZohoService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/contact/sync/sync.service.ts b/packages/api/src/crm/contact/sync/sync.service.ts index e8799ca65..36464b3a7 100644 --- a/packages/api/src/crm/contact/sync/sync.service.ts +++ b/packages/api/src/crm/contact/sync/sync.service.ts @@ -1,7 +1,6 @@ import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { ApiResponse } from '@@core/utils/types'; import { unify } from '@@core/utils/unification/unify'; import { WebhookService } from '@@core/webhook/webhook.service'; @@ -18,6 +17,7 @@ import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -39,7 +39,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -119,18 +119,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_CONTACT_SYNC_ERROR', + message: 'SyncService.syncContacts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -156,7 +163,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -212,7 +221,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -229,7 +238,7 @@ export class SyncService implements OnModuleInit { const originId = contact.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingContact = await this.prisma.crm_contacts.findFirst({ @@ -494,7 +503,7 @@ export class SyncService implements OnModuleInit { } return contacts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/deal/services/close/index.ts b/packages/api/src/crm/deal/services/close/index.ts index 36afb0dc4..ab9bfe1c7 100644 --- a/packages/api/src/crm/deal/services/close/index.ts +++ b/packages/api/src/crm/deal/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class CloseService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -94,7 +94,7 @@ export class CloseService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/deal/services/deal.service.ts b/packages/api/src/crm/deal/services/deal.service.ts index b73c13194..c2399b61a 100644 --- a/packages/api/src/crm/deal/services/deal.service.ts +++ b/packages/api/src/crm/deal/services/deal.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedDealInput, UnifiedDealOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalDealOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { IDealService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class DealService { @@ -46,7 +46,13 @@ export class DealService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_DEALS_ERROR', + message: 'DealService.batchAddDeals() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class DealService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const stage = unifiedDealData.stage_id; //check if contact_id and account_id refer to real uuids @@ -75,7 +81,9 @@ export class DealService { }, }); if (!search) - throw new Error('You inserted a stage_id which does not exist'); + throw new ReferenceError( + 'You inserted a stage_id which does not exist', + ); } const user = unifiedDealData.user_id; @@ -87,7 +95,9 @@ export class DealService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -242,7 +252,13 @@ export class DealService { ); return result_deal; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_DEAL_ERROR', + message: 'DealService.addDeal() call failed', + cause: error, + }), + ); } } @@ -311,7 +327,13 @@ export class DealService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_DEAL_ERROR', + message: 'DealService.getDeal() call failed', + cause: error, + }), + ); } } @@ -400,7 +422,13 @@ export class DealService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_DEALS_ERROR', + message: 'DealService.getDeals() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/deal/services/hubspot/index.ts b/packages/api/src/crm/deal/services/hubspot/index.ts index 7e9672693..a8191c0f6 100644 --- a/packages/api/src/crm/deal/services/hubspot/index.ts +++ b/packages/api/src/crm/deal/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -59,7 +59,7 @@ export class HubspotService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/deal/services/pipedrive/index.ts b/packages/api/src/crm/deal/services/pipedrive/index.ts index 20c931ed9..f20ba46b9 100644 --- a/packages/api/src/crm/deal/services/pipedrive/index.ts +++ b/packages/api/src/crm/deal/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveDealInput, PipedriveDealOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -91,7 +90,7 @@ export class PipedriveService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/deal/services/zendesk/index.ts b/packages/api/src/crm/deal/services/zendesk/index.ts index 7ae8b94a3..324a2c566 100644 --- a/packages/api/src/crm/deal/services/zendesk/index.ts +++ b/packages/api/src/crm/deal/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskDealInput, ZendeskDealOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -97,7 +96,7 @@ export class ZendeskService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/deal/services/zoho/index.ts b/packages/api/src/crm/deal/services/zoho/index.ts index 4ec0f9cb2..491344722 100644 --- a/packages/api/src/crm/deal/services/zoho/index.ts +++ b/packages/api/src/crm/deal/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class ZohoService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -62,7 +62,6 @@ export class ZohoService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -97,7 +96,7 @@ export class ZohoService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/deal/sync/sync.service.ts b/packages/api/src/crm/deal/sync/sync.service.ts index 9c3fa9b48..c8d25bf0b 100644 --- a/packages/api/src/crm/deal/sync/sync.service.ts +++ b/packages/api/src/crm/deal/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { crm_deals as CrmDeal } from '@prisma/client'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -108,18 +108,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_DEAL_SYNC_ERROR', + message: 'SyncService.syncDeals() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -145,7 +152,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping deals syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -203,7 +212,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -220,7 +229,7 @@ export class SyncService implements OnModuleInit { const originId = deal.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingDeal = await this.prisma.crm_deals.findFirst({ @@ -367,7 +376,7 @@ export class SyncService implements OnModuleInit { } return deals_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/engagement/services/close/index.ts b/packages/api/src/crm/engagement/services/close/index.ts index 7662a3e73..9832920e5 100644 --- a/packages/api/src/crm/engagement/services/close/index.ts +++ b/packages/api/src/crm/engagement/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -58,7 +58,7 @@ export class CloseService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -98,7 +98,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -138,7 +138,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -181,7 +181,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -208,7 +208,7 @@ export class CloseService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -246,7 +246,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -286,7 +286,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -322,7 +322,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/engagement/services/engagement.service.ts b/packages/api/src/crm/engagement/services/engagement.service.ts index 70f895d93..9279728eb 100644 --- a/packages/api/src/crm/engagement/services/engagement.service.ts +++ b/packages/api/src/crm/engagement/services/engagement.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedEngagementInput, @@ -49,7 +49,13 @@ export class EngagementService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_ENGAGEMENTS_ERROR', + message: 'EngagmentService.batchAddEngagements() call failed', + cause: error, + }), + ); } } @@ -67,7 +73,7 @@ export class EngagementService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const company = unifiedEngagementData.company_id; //check if contact_id and account_id refer to real uuids @@ -78,16 +84,18 @@ export class EngagementService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const type = unifiedEngagementData.type.toUpperCase(); if (type) { if (!ENGAGEMENTS_TYPE.includes(type)) - throw new Error( + throw new ReferenceError( 'You inserted a engagement type which does not exist', ); } else { - throw new Error('You didnt insert a type for your engagement'); + throw new ReferenceError('You didnt insert a type for your engagement'); } const engagement_contacts = unifiedEngagementData.contacts; @@ -99,7 +107,7 @@ export class EngagementService { }, }); if (!search) - throw new Error( + throw new ReferenceError( 'You inserted an id_crm_engagement_contact which does not exist', ); }); @@ -286,7 +294,13 @@ export class EngagementService { ); return result_engagement; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_ENGAGEMENT_ERROR', + message: 'EngagmentService.addEngagement() call failed', + cause: error, + }), + ); } } @@ -363,7 +377,13 @@ export class EngagementService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_ENGAGEMENT_ERROR', + message: 'EngagmentService.getEngagement() call failed', + cause: error, + }), + ); } } @@ -460,7 +480,13 @@ export class EngagementService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_ENGAGEMENTS_ERROR', + message: 'EngagmentService.getEngagements() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/engagement/services/hubspot/index.ts b/packages/api/src/crm/engagement/services/hubspot/index.ts index 82eb5fa2d..161f6f420 100644 --- a/packages/api/src/crm/engagement/services/hubspot/index.ts +++ b/packages/api/src/crm/engagement/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -103,7 +103,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -146,7 +146,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -189,7 +189,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -216,7 +216,7 @@ export class HubspotService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -262,7 +262,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -311,7 +311,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -357,7 +357,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/engagement/services/pipedrive/index.ts b/packages/api/src/crm/engagement/services/pipedrive/index.ts index 5df31add2..5ff75674d 100644 --- a/packages/api/src/crm/engagement/services/pipedrive/index.ts +++ b/packages/api/src/crm/engagement/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -44,7 +44,7 @@ export class PipedriveService implements IEngagementService { statusCode: 201, };*/ } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -70,7 +70,7 @@ export class PipedriveService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -110,7 +110,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -149,7 +149,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -188,7 +188,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/engagement/services/zendesk/index.ts b/packages/api/src/crm/engagement/services/zendesk/index.ts index 5b77b5a14..970ded332 100644 --- a/packages/api/src/crm/engagement/services/zendesk/index.ts +++ b/packages/api/src/crm/engagement/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskEngagementInput, ZendeskEngagementOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -41,7 +41,7 @@ export class ZendeskService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -83,7 +83,7 @@ export class ZendeskService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -109,7 +109,7 @@ export class ZendeskService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -148,7 +148,7 @@ export class ZendeskService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/engagement/services/zoho/index.ts b/packages/api/src/crm/engagement/services/zoho/index.ts index 81662ff87..d342fd96f 100644 --- a/packages/api/src/crm/engagement/services/zoho/index.ts +++ b/packages/api/src/crm/engagement/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoEngagementInput, ZohoEngagementOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements IEngagementService { ActionType.POST, ); } - return; } async syncEngagements( @@ -98,7 +97,7 @@ export class ZohoService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/engagement/sync/sync.service.ts b/packages/api/src/crm/engagement/sync/sync.service.ts index 9b13f3b96..bade74680 100644 --- a/packages/api/src/crm/engagement/sync/sync.service.ts +++ b/packages/api/src/crm/engagement/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,8 @@ import { OriginalEngagementOutput } from '@@core/utils/types/original/original.c import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; +import { NotFoundError } from 'rxjs'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +36,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +68,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing engagements....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -104,18 +105,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_ENGAGEMENT_SYNC_ERROR', + message: 'SyncService.syncEngagements() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -142,7 +150,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping engagements syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -204,7 +214,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -387,7 +397,7 @@ export class SyncService implements OnModuleInit { } return engagements_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/note/services/close/index.ts b/packages/api/src/crm/note/services/close/index.ts index ab278defd..d9ee7177e 100644 --- a/packages/api/src/crm/note/services/close/index.ts +++ b/packages/api/src/crm/note/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseNoteInput, CloseNoteOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class CloseService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -93,7 +93,7 @@ export class CloseService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/note/services/hubspot/index.ts b/packages/api/src/crm/note/services/hubspot/index.ts index 5d7fff184..137cf65fe 100644 --- a/packages/api/src/crm/note/services/hubspot/index.ts +++ b/packages/api/src/crm/note/services/hubspot/index.ts @@ -9,7 +9,7 @@ import { import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/note/services/note.service.ts b/packages/api/src/crm/note/services/note.service.ts index 7157bd1a9..79c181132 100644 --- a/packages/api/src/crm/note/services/note.service.ts +++ b/packages/api/src/crm/note/services/note.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedNoteInput, UnifiedNoteOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalNoteOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { INoteService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class NoteService { @@ -46,7 +46,13 @@ export class NoteService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_NOTES_ERROR', + message: 'NoteService.batchAddNotes() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class NoteService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const user = unifiedNoteData.user_id; if (user) { @@ -74,7 +80,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const company = unifiedNoteData.company_id; @@ -86,7 +94,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a company_id which does not exist'); + throw new ReferenceError( + 'You inserted a company_id which does not exist', + ); } const contact = unifiedNoteData.contact_id; //check if contact_id and account_id refer to real uuids @@ -97,7 +107,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const deal = unifiedNoteData.deal_id; @@ -109,7 +121,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a deal_id which does not exist'); + throw new ReferenceError( + 'You inserted a deal_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -257,7 +271,13 @@ export class NoteService { ); return result_note; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_NOTE_ERROR', + message: 'NoteService.addNote()) call failed', + cause: error, + }), + ); } } @@ -326,7 +346,13 @@ export class NoteService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_NOTE_ERROR', + message: 'NoteService.getNote() call failed', + cause: error, + }), + ); } } @@ -415,7 +441,13 @@ export class NoteService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_NOTES_ERROR', + message: 'NoteService.getNotes() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/note/services/pipedrive/index.ts b/packages/api/src/crm/note/services/pipedrive/index.ts index b18c3dd27..9be8eb573 100644 --- a/packages/api/src/crm/note/services/pipedrive/index.ts +++ b/packages/api/src/crm/note/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveNoteInput, PipedriveNoteOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -91,7 +90,7 @@ export class PipedriveService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/note/services/zendesk/index.ts b/packages/api/src/crm/note/services/zendesk/index.ts index 0d8e481d0..d40df6071 100644 --- a/packages/api/src/crm/note/services/zendesk/index.ts +++ b/packages/api/src/crm/note/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskNoteInput, ZendeskNoteOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -97,7 +96,7 @@ export class ZendeskService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/note/services/zoho/index.ts b/packages/api/src/crm/note/services/zoho/index.ts index 1dc7ab52d..566b2b404 100644 --- a/packages/api/src/crm/note/services/zoho/index.ts +++ b/packages/api/src/crm/note/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoNoteInput, ZohoNoteOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -97,7 +96,7 @@ export class ZohoService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/note/sync/sync.service.ts b/packages/api/src/crm/note/sync/sync.service.ts index 9ce345aa0..5d6ea7296 100644 --- a/packages/api/src/crm/note/sync/sync.service.ts +++ b/packages/api/src/crm/note/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalNoteOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing notes....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_NOTE_SYNC_ERROR', + message: 'SyncService.syncNotes() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping notes syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +205,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +222,7 @@ export class SyncService implements OnModuleInit { const originId = note.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingNote = await this.prisma.crm_notes.findFirst({ @@ -347,7 +356,7 @@ export class SyncService implements OnModuleInit { } return notes_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/stage/services/close/index.ts b/packages/api/src/crm/stage/services/close/index.ts index d7d8b4eeb..11617dfad 100644 --- a/packages/api/src/crm/stage/services/close/index.ts +++ b/packages/api/src/crm/stage/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseStageOutput, commonStageCloseProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -58,7 +58,7 @@ export class CloseService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/stage/services/hubspot/index.ts b/packages/api/src/crm/stage/services/hubspot/index.ts index 9a1e049d7..8244be028 100644 --- a/packages/api/src/crm/stage/services/hubspot/index.ts +++ b/packages/api/src/crm/stage/services/hubspot/index.ts @@ -5,7 +5,7 @@ import { HubspotStageOutput, commonStageHubspotProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -68,7 +68,7 @@ export class HubspotService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/stage/services/pipedrive/index.ts b/packages/api/src/crm/stage/services/pipedrive/index.ts index 83089432d..b74f3e794 100644 --- a/packages/api/src/crm/stage/services/pipedrive/index.ts +++ b/packages/api/src/crm/stage/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveStageOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -71,7 +71,7 @@ export class PipedriveService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/stage/services/stage.service.ts b/packages/api/src/crm/stage/services/stage.service.ts index a7d4d4c48..03728afef 100644 --- a/packages/api/src/crm/stage/services/stage.service.ts +++ b/packages/api/src/crm/stage/services/stage.service.ts @@ -2,11 +2,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; -import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedStageOutput } from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class StageService { @@ -75,7 +72,13 @@ export class StageService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_STAGE_ERROR', + message: 'StageService.getStage() call failed', + cause: error, + }), + ); } } @@ -160,7 +163,13 @@ export class StageService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_STAGES_ERROR', + message: 'StageService.getStages() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/stage/services/zendesk/index.ts b/packages/api/src/crm/stage/services/zendesk/index.ts index f6eb415b2..5d5bb5dc4 100644 --- a/packages/api/src/crm/stage/services/zendesk/index.ts +++ b/packages/api/src/crm/stage/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskStageOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -71,7 +71,7 @@ export class ZendeskService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/stage/services/zoho/index.ts b/packages/api/src/crm/stage/services/zoho/index.ts index 97b28db9c..7ee5a1509 100644 --- a/packages/api/src/crm/stage/services/zoho/index.ts +++ b/packages/api/src/crm/stage/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoStageOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -57,7 +57,7 @@ export class ZohoService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/stage/sync/sync.service.ts b/packages/api/src/crm/stage/sync/sync.service.ts index 1c3974ed1..c834c8152 100644 --- a/packages/api/src/crm/stage/sync/sync.service.ts +++ b/packages/api/src/crm/stage/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalStageOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing stages....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -112,21 +112,28 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_STAGE_SYNC_ERROR', + message: 'SyncService.syncStages() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -153,7 +160,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping stages syncing... No ${integrationId} connection was found for linked stage ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -213,7 +222,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -231,7 +240,7 @@ export class SyncService implements OnModuleInit { const originId = stage.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingStage = await this.prisma.crm_deals.findFirst({ @@ -376,7 +385,7 @@ export class SyncService implements OnModuleInit { } return stages_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/task/services/close/index.ts b/packages/api/src/crm/task/services/close/index.ts index 49ca59e24..6ce71f88c 100644 --- a/packages/api/src/crm/task/services/close/index.ts +++ b/packages/api/src/crm/task/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseTaskInput, CloseTaskOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class CloseService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -93,7 +93,7 @@ export class CloseService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/task/services/hubspot/index.ts b/packages/api/src/crm/task/services/hubspot/index.ts index a1223aaa2..b08c28947 100644 --- a/packages/api/src/crm/task/services/hubspot/index.ts +++ b/packages/api/src/crm/task/services/hubspot/index.ts @@ -9,7 +9,7 @@ import { import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/task/services/pipedrive/index.ts b/packages/api/src/crm/task/services/pipedrive/index.ts index c67e53de8..19a5809f9 100644 --- a/packages/api/src/crm/task/services/pipedrive/index.ts +++ b/packages/api/src/crm/task/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveTaskInput, PipedriveTaskOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -94,7 +93,7 @@ export class PipedriveService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/task/services/task.service.ts b/packages/api/src/crm/task/services/task.service.ts index b0d8c3b71..074218062 100644 --- a/packages/api/src/crm/task/services/task.service.ts +++ b/packages/api/src/crm/task/services/task.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedTaskInput, UnifiedTaskOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalTaskOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { ITaskService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class TaskService { @@ -46,7 +46,13 @@ export class TaskService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_TASKS_ERROR', + message: 'TaskService.batchAddTasks() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class TaskService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const company = unifiedTaskData.company_id; //check if contact_id and account_id refer to real uuids @@ -75,7 +81,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a company_id which does not exist'); + throw new ReferenceError( + 'You inserted a company_id which does not exist', + ); } const user = unifiedTaskData.user_id; //check if contact_id and account_id refer to real uuids @@ -86,7 +94,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const deal = unifiedTaskData.deal_id; @@ -98,7 +108,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a deal_id which does not exist'); + throw new ReferenceError( + 'You inserted a deal_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -263,7 +275,13 @@ export class TaskService { ); return result_task; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_TASK_ERROR', + message: 'TaskService.addTask() call failed', + cause: error, + }), + ); } } @@ -334,7 +352,13 @@ export class TaskService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_TASK_ERROR', + message: 'TaskService.getTask() call failed', + cause: error, + }), + ); } } @@ -425,7 +449,13 @@ export class TaskService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_TASKS_ERROR', + message: 'TaskService.getTasks() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/task/services/zendesk/index.ts b/packages/api/src/crm/task/services/zendesk/index.ts index 915074e52..64fbb6b25 100644 --- a/packages/api/src/crm/task/services/zendesk/index.ts +++ b/packages/api/src/crm/task/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskTaskInput, ZendeskTaskOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class ZendeskService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -68,7 +68,6 @@ export class ZendeskService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -101,7 +100,7 @@ export class ZendeskService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/task/services/zoho/index.ts b/packages/api/src/crm/task/services/zoho/index.ts index 1a20399a1..d6fe98c98 100644 --- a/packages/api/src/crm/task/services/zoho/index.ts +++ b/packages/api/src/crm/task/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoTaskInput, ZohoTaskOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -98,7 +97,7 @@ export class ZohoService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/task/sync/sync.service.ts b/packages/api/src/crm/task/sync/sync.service.ts index e33555cf5..0b5d56064 100644 --- a/packages/api/src/crm/task/sync/sync.service.ts +++ b/packages/api/src/crm/task/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalTaskOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing tasks....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_TASK_SYNC_ERROR', + message: 'SyncService.syncTasks() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tasks syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +205,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +222,7 @@ export class SyncService implements OnModuleInit { const originId = task.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTask = await this.prisma.crm_tasks.findFirst({ @@ -365,7 +374,7 @@ export class SyncService implements OnModuleInit { } return tasks_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/user/services/close/index.ts b/packages/api/src/crm/user/services/close/index.ts index d9ad8981b..727c7a2d5 100644 --- a/packages/api/src/crm/user/services/close/index.ts +++ b/packages/api/src/crm/user/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseUserOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class CloseService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/user/services/hubspot/index.ts b/packages/api/src/crm/user/services/hubspot/index.ts index e99f6d9d4..a9f04624c 100644 --- a/packages/api/src/crm/user/services/hubspot/index.ts +++ b/packages/api/src/crm/user/services/hubspot/index.ts @@ -5,7 +5,7 @@ import { HubspotUserOutput, commonUserHubspotProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -64,7 +64,7 @@ export class HubspotService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/user/services/pipedrive/index.ts b/packages/api/src/crm/user/services/pipedrive/index.ts index a3f0eaa76..7c2daa98f 100644 --- a/packages/api/src/crm/user/services/pipedrive/index.ts +++ b/packages/api/src/crm/user/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveUserOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -50,7 +50,7 @@ export class PipedriveService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/user/services/user.service.ts b/packages/api/src/crm/user/services/user.service.ts index 93b1fb52f..e646fd07c 100644 --- a/packages/api/src/crm/user/services/user.service.ts +++ b/packages/api/src/crm/user/services/user.service.ts @@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedUserOutput } from '../types/model.unified'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class UserService { @@ -82,7 +82,13 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_USER_ERROR', + message: 'UserService.getUser() call failed', + cause: error, + }), + ); } } @@ -168,7 +174,13 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_USERS_ERROR', + message: 'UserService.getUsers() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/user/services/zendesk/index.ts b/packages/api/src/crm/user/services/zendesk/index.ts index f9ad2d85f..e0afe1d00 100644 --- a/packages/api/src/crm/user/services/zendesk/index.ts +++ b/packages/api/src/crm/user/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskUserOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class ZendeskService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/user/services/zoho/index.ts b/packages/api/src/crm/user/services/zoho/index.ts index de3c52ac1..430eaeecf 100644 --- a/packages/api/src/crm/user/services/zoho/index.ts +++ b/packages/api/src/crm/user/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoUserOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class ZohoService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/user/sync/sync.service.ts b/packages/api/src/crm/user/sync/sync.service.ts index f5832a08c..2207dd656 100644 --- a/packages/api/src/crm/user/sync/sync.service.ts +++ b/packages/api/src/crm/user/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalUserOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing users....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_USER_SYNC_ERROR', + message: 'SyncService.syncUsers() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +205,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +222,7 @@ export class SyncService implements OnModuleInit { const originId = user.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingUser = await this.prisma.crm_users.findFirst({ @@ -331,7 +340,7 @@ export class SyncService implements OnModuleInit { } return users_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/@lib/@utils/index.ts b/packages/api/src/ticketing/@lib/@utils/index.ts index 307bb5912..ec18ed174 100644 --- a/packages/api/src/ticketing/@lib/@utils/index.ts +++ b/packages/api/src/ticketing/@lib/@utils/index.ts @@ -6,9 +6,6 @@ export class Utils { constructor() { this.prisma = new PrismaClient(); - /*this.cryptoService = new EncryptionService( - new EnvironmentService(new ConfigService()), - );*/ } async fetchFileStreamFromURL(file_url: string) { @@ -23,10 +20,10 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) throw ReferenceError('Tcg_User Undefined'); return res.id_tcg_user; } catch (error) { - throw new Error(error); + throw error; } } @@ -37,10 +34,10 @@ export class Utils { id_tcg_user: uuid, }, }); - if (!res) throw new Error(`tcg_user not found for uuid ${uuid}`); + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -52,10 +49,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_account not found for remote_id ${remote_id}`, + ); return res.id_tcg_contact; } catch (error) { - throw new Error(error); + throw error; } } @@ -66,10 +66,11 @@ export class Utils { id_tcg_contact: uuid, }, }); - if (!res) throw new Error(`tcg_contact not found for uuid ${uuid}`); + if (!res) + throw new ReferenceError(`tcg_contact not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -80,13 +81,10 @@ export class Utils { id_tcg_user: uuid, }, }); - if (!res) return; - /*throw new Error( - `tcg_user not found for uuid ${uuid} and integration ${remote_platform}`, - );*/ + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -97,10 +95,10 @@ export class Utils { id_tcg_user: uuid, }, }); - if (!res) throw new Error(`tcg_user not found for uuid ${uuid}`); + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.email_address; } catch (error) { - throw new Error(error); + throw error; } } @@ -115,10 +113,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_collection not found for remote_id ${remote_id}`, + ); return res.id_tcg_collection; } catch (error) { - throw new Error(error); + throw error; } } @@ -129,10 +130,11 @@ export class Utils { id_tcg_collection: uuid, }, }); - if (!res) return; + if (!res) + throw new ReferenceError(`tcg_collection not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -144,10 +146,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_ticket not found for remote_id ${remote_id}`, + ); return res.id_tcg_ticket; } catch (error) { - throw new Error(error); + throw error; } } @@ -158,10 +163,11 @@ export class Utils { id_tcg_ticket: uuid, }, }); - if (!res) throw new Error(`tcg_contact not found for uuid ${uuid}`); + if (!res) + throw new ReferenceError(`tcg_contact not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } } diff --git a/packages/api/src/ticketing/@webhook/zendesk/handler.ts b/packages/api/src/ticketing/@webhook/zendesk/handler.ts index 2b49f5bb0..4661e1818 100644 --- a/packages/api/src/ticketing/@webhook/zendesk/handler.ts +++ b/packages/api/src/ticketing/@webhook/zendesk/handler.ts @@ -7,6 +7,13 @@ import axios from 'axios'; import { Payload } from './types'; import { mapToRemoteEvent } from './utils'; import * as crypto from 'crypto'; +import { + Action, + ActionType, + handle3rdPartyServiceError, + ManagedWebhooksError, + throwTypedError, +} from '@@core/utils/errors'; @Injectable() export class ZendeskHandlerService { @@ -20,220 +27,250 @@ export class ZendeskHandlerService { } async createWebhook(data: { [key: string]: any }, mw_ids: string[]) { - if (mw_ids[0]) { - await this.createBasicWebhook(data.name_basic, mw_ids[0]); - } - if (mw_ids[1]) { - await this.createTriggerWebhook(data.name_trigger, mw_ids[1]); + try { + if (mw_ids[0]) { + await this.createBasicWebhook(data.name_basic, mw_ids[0]); + } + if (mw_ids[1]) { + await this.createTriggerWebhook(data.name_trigger, mw_ids[1]); + } + } catch (error) { + throw error; } } async createBasicWebhook(webhook_name: string, mw_id: string) { - const mw = await this.prisma.managed_webhooks.findUnique({ - where: { - id_managed_webhook: mw_id, - }, - }); - const conn = await this.prisma.connections.findUnique({ - where: { - id_connection: mw.id_connection, - }, - }); - const unified_events = mw.active_events; + try { + const mw = await this.prisma.managed_webhooks.findUnique({ + where: { + id_managed_webhook: mw_id, + }, + }); + if (!mw) throw ReferenceError('Managed Webhook undefined'); + const conn = await this.prisma.connections.findUnique({ + where: { + id_connection: mw.id_connection, + }, + }); + if (!conn) throw ReferenceError('Connection undefined'); + const unified_events = mw.active_events; - const events_ = unified_events - .flatMap((event) => mapToRemoteEvent(event)) - .filter((item) => item !== null && item !== undefined); + const events_ = unified_events + .flatMap((event) => mapToRemoteEvent(event)) + .filter((item) => item !== null && item !== undefined); - const body_data = { - webhook: { - name: webhook_name, - status: 'active', - endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, - http_method: 'POST', - request_format: 'json', - subscriptions: events_, - }, - }; + const body_data = { + webhook: { + name: webhook_name, + status: 'active', + endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, + http_method: 'POST', + request_format: 'json', + subscriptions: events_, + }, + }; - this.logger.log('Creating basic webhook... '); + this.logger.log('Creating basic webhook... '); - const resp = await axios.post( - `${conn.account_url}/webhooks`, - JSON.stringify(body_data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const resp = await axios.post( + `${conn.account_url}/webhooks`, + JSON.stringify(body_data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); + ); - this.logger.log( - 'Zendesk basic webhook created ' + JSON.stringify(resp.data), - ); + this.logger.log( + 'Zendesk basic webhook created ' + JSON.stringify(resp.data), + ); - this.logger.log('Fetching basic webhook secret... '); + this.logger.log('Fetching basic webhook secret... '); - const webhook_result = await axios.get( - `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const webhook_result = await axios.get( + `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, + }, + ); + //update signing secret inside mw table + await this.prisma.managed_webhooks.update({ + where: { + id_managed_webhook: mw.id_managed_webhook, }, - }, - ); - //update signing secret inside mw table - await this.prisma.managed_webhooks.update({ - where: { - id_managed_webhook: mw.id_managed_webhook, - }, - data: { - remote_signing_secret: webhook_result.data.signing_secret.secret, - }, - }); + data: { + remote_signing_secret: webhook_result.data.signing_secret.secret, + }, + }); + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'zendesk', + Action.webhookCreation, + ActionType.POST, + ); + } } async createTriggerWebhook(webhook_name: string, mw_id: string) { - const mw = await this.prisma.managed_webhooks.findUnique({ - where: { - id_managed_webhook: mw_id, - }, - }); - const conn = await this.prisma.connections.findUnique({ - where: { - id_connection: mw.id_connection, - }, - }); - const body_data = { - webhook: { - name: webhook_name, - status: 'active', - endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, - http_method: 'POST', - request_format: 'json', - subscriptions: ['conditional_ticket_events'], - }, - }; + try { + const mw = await this.prisma.managed_webhooks.findUnique({ + where: { + id_managed_webhook: mw_id, + }, + }); + if (!mw) throw ReferenceError('Managed Webhook undefined'); - this.logger.log('Creating trigger webhook... '); - const resp = await axios.post( - `${conn.account_url}/webhooks`, - JSON.stringify(body_data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const conn = await this.prisma.connections.findUnique({ + where: { + id_connection: mw.id_connection, }, - }, - ); + }); + if (!conn) throw ReferenceError('Connection undefined'); - this.logger.log( - 'Zendesk trigger webhook created ' + JSON.stringify(resp.data), - ); + const body_data = { + webhook: { + name: webhook_name, + status: 'active', + endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, + http_method: 'POST', + request_format: 'json', + subscriptions: ['conditional_ticket_events'], + }, + }; - // create trigger webhook - const b_ = { - trigger: { - actions: [ - { - field: 'notification_webhook', - value: [ - resp.data.webhook.id, - ` - { - "id_ticket": "{{ticket.id}}" - } - `, - ], + this.logger.log('Creating trigger webhook... '); + const resp = await axios.post( + `${conn.account_url}/webhooks`, + JSON.stringify(body_data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, }, - ], - conditions: { - any: [ - { - field: 'assignee_id', - operator: 'changed', - }, - { - field: 'attachment', - operator: 'is', - value: 'present', - }, - { - field: 'comment_is_public', - value: 'true', - }, - { - field: 'priority', - operator: 'changed', - }, - { - field: 'status', - value: 'changed', - }, - { - field: 'update_type', - value: 'Create', - }, - { - field: 'update_type', - value: 'Change', - }, - { - field: 'cc', - operator: 'is', - value: 'present', - }, + }, + ); + + this.logger.log( + 'Zendesk trigger webhook created ' + JSON.stringify(resp.data), + ); + + // create trigger webhook + const b_ = { + trigger: { + actions: [ { - field: 'type', - operator: 'changed', + field: 'notification_webhook', + value: [ + resp.data.webhook.id, + ` + { + "id_ticket": "{{ticket.id}}" + } + `, + ], }, ], + conditions: { + any: [ + { + field: 'assignee_id', + operator: 'changed', + }, + { + field: 'attachment', + operator: 'is', + value: 'present', + }, + { + field: 'comment_is_public', + value: 'true', + }, + { + field: 'priority', + operator: 'changed', + }, + { + field: 'status', + value: 'changed', + }, + { + field: 'update_type', + value: 'Create', + }, + { + field: 'update_type', + value: 'Change', + }, + { + field: 'cc', + operator: 'is', + value: 'present', + }, + { + field: 'type', + operator: 'changed', + }, + ], + }, + title: 'Trigger Webhooks', }, - title: 'Trigger Webhooks', - }, - }; - const trigger_result = await axios.post( - `${conn.account_url}/triggers.json`, - JSON.stringify(b_), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + }; + const trigger_result = await axios.post( + `${conn.account_url}/triggers.json`, + JSON.stringify(b_), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); + ); - this.logger.log('Fetching trigger webhook secret... '); - const webhook_result = await axios.get( - `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + this.logger.log('Fetching trigger webhook secret... '); + const webhook_result = await axios.get( + `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); - //update signing secret inside mw table - await this.prisma.managed_webhooks.update({ - where: { - id_managed_webhook: mw.id_managed_webhook, - }, - data: { - remote_signing_secret: webhook_result.data.signing_secret.secret, - }, - }); + ); + //update signing secret inside mw table + await this.prisma.managed_webhooks.update({ + where: { + id_managed_webhook: mw.id_managed_webhook, + }, + data: { + remote_signing_secret: webhook_result.data.signing_secret.secret, + }, + }); + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'zendesk', + Action.webhookCreation, + ActionType.POST, + ); + } } async handler(payload: Payload, headers: any, id_managed_webhook: string) { @@ -251,7 +288,14 @@ export class ZendeskHandlerService { //non-ticket payload } } catch (error) { - throw new Error(error); + throwTypedError( + new ManagedWebhooksError({ + name: 'RECEIVING_WEBHOOK_PAYLOAD_ERROR', + message: 'ZendeskHandlerService.handler() call failed', + cause: error, + }), + this.logger, + ); } } @@ -275,7 +319,15 @@ export class ZendeskHandlerService { const sig = hmac.update(timestamp + body).digest('base64'); return Buffer.compare(Buffer.from(signature), Buffer.from(sig)) === 0; } catch (error) { - throw new Error(error); + throwTypedError( + new ManagedWebhooksError({ + name: 'SIGNATURE_VERIFICATION_AUTHENTICITY_ERROR', + message: + 'ZendeskHandlerService.verifyWebhookAuthenticity() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/ticketing/account/services/account.service.ts b/packages/api/src/ticketing/account/services/account.service.ts index 529a89239..c724c5aa5 100644 --- a/packages/api/src/ticketing/account/services/account.service.ts +++ b/packages/api/src/ticketing/account/services/account.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; import { UnifiedAccountOutput } from '../types/model.unified'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; @Injectable() export class AccountService { @@ -72,7 +72,13 @@ export class AccountService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ACCOUNT_ERROR', + message: 'AccountService.getAccount() call failed', + cause: error, + }), + ); } } @@ -159,7 +165,13 @@ export class AccountService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ACCOUNTS_ERROR', + message: 'AccountService.getAccounts() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/account/services/front/index.ts b/packages/api/src/ticketing/account/services/front/index.ts index 4295f41f5..9d84fa9ed 100644 --- a/packages/api/src/ticketing/account/services/front/index.ts +++ b/packages/api/src/ticketing/account/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IAccountService } from '@ticketing/account/types'; import { FrontAccountOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements IAccountService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.account, ActionType.GET, ); diff --git a/packages/api/src/ticketing/account/services/zendesk/index.ts b/packages/api/src/ticketing/account/services/zendesk/index.ts index 895ffca17..97e285bba 100644 --- a/packages/api/src/ticketing/account/services/zendesk/index.ts +++ b/packages/api/src/ticketing/account/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IAccountService } from '@ticketing/account/types'; @@ -58,10 +58,10 @@ export class ZendeskService implements IAccountService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.account, ActionType.GET, ); diff --git a/packages/api/src/ticketing/account/sync/sync.service.ts b/packages/api/src/ticketing/account/sync/sync.service.ts index ca6ac5e12..de10b5c5d 100644 --- a/packages/api/src/ticketing/account/sync/sync.service.ts +++ b/packages/api/src/ticketing/account/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing accounts....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_ACCOUNT_SYNC_ERROR', + message: 'SyncService.syncAccounts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping accounts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name @@ -194,7 +203,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -211,7 +220,7 @@ export class SyncService implements OnModuleInit { const originId = account.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingAccount = await this.prisma.tcg_accounts.findFirst({ @@ -317,7 +326,7 @@ export class SyncService implements OnModuleInit { } return accounts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/attachment/services/attachment.service.ts b/packages/api/src/ticketing/attachment/services/attachment.service.ts index ea1649abf..5e6fe6d6f 100644 --- a/packages/api/src/ticketing/attachment/services/attachment.service.ts +++ b/packages/api/src/ticketing/attachment/services/attachment.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedAttachmentInput, @@ -40,7 +40,13 @@ export class AttachmentService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_ATTACHMENTS_ERROR', + message: 'AttachmentService.batchAddAttachments() call failed', + cause: error, + }), + ); } } @@ -56,7 +62,7 @@ export class AttachmentService { id_linked_user: linkedUserId, }, }); - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); //EXCEPTION: for Attachments we directly store them inside our db (no raw call to the provider) //the actual job to retrieve the attachment info would be done inside /comments @@ -132,7 +138,13 @@ export class AttachmentService { ); return result_attachment; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_ATTACHMENT_ERROR', + message: 'AttachmentService.addAttachment() call failed', + cause: error, + }), + ); } } @@ -198,7 +210,13 @@ export class AttachmentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ATTACHMENT_ERROR', + message: 'AttachmentService.getAttachment() call failed', + cause: error, + }), + ); } } @@ -287,7 +305,13 @@ export class AttachmentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ATTACHMENTS_ERROR', + message: 'AttachmentService.getAttachments() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/ticketing/attachment/services/registry.service.ts b/packages/api/src/ticketing/attachment/services/registry.service.ts index 8a9f8f360..56484d3bd 100644 --- a/packages/api/src/ticketing/attachment/services/registry.service.ts +++ b/packages/api/src/ticketing/attachment/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IAttachmentService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/collection/services/collection.service.ts b/packages/api/src/ticketing/collection/services/collection.service.ts index 58d23f808..983f2a946 100644 --- a/packages/api/src/ticketing/collection/services/collection.service.ts +++ b/packages/api/src/ticketing/collection/services/collection.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCollectionOutput } from '../types/model.unified'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; @@ -58,7 +58,11 @@ export class CollectionService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_COLLECTION_ERROR", + message: "CollectionService.getCollection() call failed", + cause: error + })) } } @@ -120,7 +124,11 @@ export class CollectionService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_COLLECTIONS_ERROR", + message: "CollectionService.getCollections() call failed", + cause: error + })) } } } diff --git a/packages/api/src/ticketing/collection/services/gitlab/index.ts b/packages/api/src/ticketing/collection/services/gitlab/index.ts index 492093cf9..e397f0b7c 100644 --- a/packages/api/src/ticketing/collection/services/gitlab/index.ts +++ b/packages/api/src/ticketing/collection/services/gitlab/index.ts @@ -5,86 +5,85 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ICollectionService } from '@ticketing/collection/types'; import { GitlabCollectionInput, GitlabCollectionOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; @Injectable() export class GitlabService implements ICollectionService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.collection.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } - - async syncCollections( - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.collection.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } - // It fetches all project from gitlab - // const resp = await axios.get(`${connection.account_url}/projects`, { - // headers: { - // 'Content-Type': 'application/json', - // Authorization: `Bearer ${this.cryptoService.decrypt( - // connection.access_token, - // )}`, - // }, - // }); + async syncCollections( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const currentUser = await axios.get(`${connection.account_url}/user`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }) + // It fetches all project from gitlab + // const resp = await axios.get(`${connection.account_url}/projects`, { + // headers: { + // 'Content-Type': 'application/json', + // Authorization: `Bearer ${this.cryptoService.decrypt( + // connection.access_token, + // )}`, + // }, + // }); - const resp = await axios.get(`${connection.account_url}/users/${currentUser.data.id}/projects`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }) + const currentUser = await axios.get(`${connection.account_url}/user`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); - this.logger.log(`Synced gitlab collections !`); + const resp = await axios.get( + `${connection.account_url}/users/${currentUser.data.id}/projects`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); - // console.log("In index of gitlab", JSON.stringify(resp.data)) + this.logger.log(`Synced gitlab collections !`); + // console.log("In index of gitlab", JSON.stringify(resp.data)) - return { - data: resp.data, - message: 'Gitlab collections retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.collection, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'Gitlab collections retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.collection, + ActionType.GET, + ); } - - + } } diff --git a/packages/api/src/ticketing/collection/services/jira/index.ts b/packages/api/src/ticketing/collection/services/jira/index.ts index 7b09a4856..da356b9eb 100644 --- a/packages/api/src/ticketing/collection/services/jira/index.ts +++ b/packages/api/src/ticketing/collection/services/jira/index.ts @@ -5,11 +5,10 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ICollectionService } from '@ticketing/collection/types'; import { JiraCollectionOutput, JiraCollectionInput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; @Injectable() export class JiraService implements ICollectionService { @@ -53,15 +52,13 @@ export class JiraService implements ICollectionService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.collection, ActionType.GET, ); } } - - } diff --git a/packages/api/src/ticketing/collection/sync/sync.service.ts b/packages/api/src/ticketing/collection/sync/sync.service.ts index 84b70ddb8..99b22243b 100644 --- a/packages/api/src/ticketing/collection/sync/sync.service.ts +++ b/packages/api/src/ticketing/collection/sync/sync.service.ts @@ -1,11 +1,10 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from '../services/registry.service'; import { unify } from '@@core/utils/unification/unify'; import { TicketingObject } from '@ticketing/@lib/@types'; @@ -34,7 +33,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -66,12 +65,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing collections....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -98,18 +97,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_COLLECTION_SYNC_ERROR', + message: 'SyncService.syncCollections() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -135,7 +141,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping collections syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } const service: ICollectionService = @@ -183,7 +191,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -195,13 +203,13 @@ export class SyncService implements OnModuleInit { ): Promise { try { let collections_results: TicketingCollection[] = []; - console.log(collections) + console.log(collections); for (let i = 0; i < collections.length; i++) { const collection = collections[i]; const originId = collection.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTeam = await this.prisma.tcg_collections.findFirst({ @@ -270,7 +278,7 @@ export class SyncService implements OnModuleInit { } return collections_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/comment/services/comment.service.ts b/packages/api/src/ticketing/comment/services/comment.service.ts index cd7d41984..ca3e5c1a9 100644 --- a/packages/api/src/ticketing/comment/services/comment.service.ts +++ b/packages/api/src/ticketing/comment/services/comment.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCommentInput, @@ -47,7 +47,13 @@ export class CommentService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_COMMENTS_ERROR', + message: 'CommentService.addComments() call failed', + cause: error, + }), + ); } } @@ -65,7 +71,7 @@ export class CommentService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const tick = unifiedCommentData.ticket_id; //check if contact_id and account_id refer to real uuids if (tick) { @@ -75,9 +81,11 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a ticket_id which does not exist'); + throw new ReferenceError( + 'You inserted a ticket_id which does not exist', + ); } else { - throw new Error( + throw new ReferenceError( 'You must attach your comment to a ticket, specify a ticket_id', ); } @@ -91,7 +99,9 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const user = unifiedCommentData.user_id; //check if contact_id and account_id refer to real uuids @@ -102,7 +112,9 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const attachmts = unifiedCommentData.attachments; @@ -115,7 +127,7 @@ export class CommentService { }, }); if (!search) - throw new Error( + throw new ReferenceError( 'You inserted an attachment_id which does not exist', ); }); @@ -142,7 +154,7 @@ export class CommentService { }, }); if (!ticket) - throw new Error( + throw new ReferenceError( 'ticket does not exist for the comment you try to create', ); const resp: ApiResponse = await service.addComment( @@ -290,7 +302,13 @@ export class CommentService { ); return result_comment; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_COMMENT_ERROR', + message: 'CommentService.addComment() call failed', + cause: error, + }), + ); } } @@ -364,7 +382,13 @@ export class CommentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COMMENT_ERROR', + message: 'CommentService.getComment() call failed', + cause: error, + }), + ); } } @@ -457,7 +481,13 @@ export class CommentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COMMENTS_ERROR', + message: 'CommentService.getComments() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/comment/services/front/index.ts b/packages/api/src/ticketing/comment/services/front/index.ts index a04bb7d85..8ec9e507e 100644 --- a/packages/api/src/ticketing/comment/services/front/index.ts +++ b/packages/api/src/ticketing/comment/services/front/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { FrontCommentInput, FrontCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; @Injectable() export class FrontService implements ICommentService { @@ -62,7 +62,9 @@ export class FrontService implements ICommentService { }, }); if (!attachment) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // TODO: Construct the right binary attachment // Get the AWS S3 right file @@ -120,10 +122,10 @@ export class FrontService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.comment, ActionType.POST, ); @@ -170,10 +172,10 @@ export class FrontService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/gitlab/index.ts b/packages/api/src/ticketing/comment/services/gitlab/index.ts index e045cfb27..fc0914e74 100644 --- a/packages/api/src/ticketing/comment/services/gitlab/index.ts +++ b/packages/api/src/ticketing/comment/services/gitlab/index.ts @@ -4,199 +4,207 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { GitlabCommentInput, GitlabCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() export class GitlabService implements ICommentService { - private readonly utils: Utils; - - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.comment.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - this.utils = new Utils(); - } - - async addComment( - commentData: GitlabCommentInput, - linkedUserId: string, - remoteIdTicket: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - - let uploads = []; - const uuids = commentData.attachment as any[]; - if (uuids && uuids.length > 0) { - const attachmentPromises = uuids.map(async (uuid) => { - const res = await this.prisma.tcg_attachments.findUnique({ - where: { - id_tcg_attachment: uuid.extra, - }, - }); - if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); - } - // Assuming you want to construct the right binary attachment here - // For now, we'll just return the URL - const stats = fs.statSync(res.file_url); - return { - url: res.file_url, - name: res.file_name, - size: stats.size, - content_type: 'application/pdf', //todo - }; - }); - uploads = await Promise.all(attachmentPromises); - } - - // Assuming you want to modify the comment object here - // For now, we'll just add the uploads to the comment - const data = { - ...commentData, - attachments: uploads, - }; - - const ticket = await this.prisma.tcg_tickets.findFirst({ - where: { - remote_id: remoteIdTicket, - remote_platform: 'gitlab', - }, - select: { - collections: true, - id_tcg_ticket: true - } - }); - - // const ticket = await this.prisma.tcg_tickets.findUnique({ - // where: { - // id_tcg_ticket: remoteIdTicket, - // }, - // select: { - // collections: true - // }, - // }); - - const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid(ticket.collections[0]); - - // Retrieve the uuid of issue from remote_data - const remote_data = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: ticket.id_tcg_ticket, - }, - }); - const { iid } = JSON.parse(remote_data.data); - - const resp = await axios.post( - `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, - JSON.stringify(data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - - return { - data: resp.data, - message: 'Gitlab comment created', - statusCode: 201, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.comment, - ActionType.POST, + private readonly utils: Utils; + + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.comment.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + this.utils = new Utils(); + } + + async addComment( + commentData: GitlabCommentInput, + linkedUserId: string, + remoteIdTicket: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + + let uploads = []; + const uuids = commentData.attachment as any[]; + if (uuids && uuids.length > 0) { + const attachmentPromises = uuids.map(async (uuid) => { + const res = await this.prisma.tcg_attachments.findUnique({ + where: { + id_tcg_attachment: uuid.extra, + }, + }); + if (!res) { + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, ); - } + } + // Assuming you want to construct the right binary attachment here + // For now, we'll just return the URL + const stats = fs.statSync(res.file_url); + return { + url: res.file_url, + name: res.file_name, + size: stats.size, + content_type: 'application/pdf', //todo + }; + }); + uploads = await Promise.all(attachmentPromises); + } + + // Assuming you want to modify the comment object here + // For now, we'll just add the uploads to the comment + const data = { + ...commentData, + attachments: uploads, + }; + + const ticket = await this.prisma.tcg_tickets.findFirst({ + where: { + remote_id: remoteIdTicket, + remote_platform: 'gitlab', + }, + select: { + collections: true, + id_tcg_ticket: true, + }, + }); + + // const ticket = await this.prisma.tcg_tickets.findUnique({ + // where: { + // id_tcg_ticket: remoteIdTicket, + // }, + // select: { + // collections: true + // }, + // }); + + const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid( + ticket.collections[0], + ); + + // Retrieve the uuid of issue from remote_data + const remote_data = await this.prisma.remote_data.findFirst({ + where: { + ressource_owner_id: ticket.id_tcg_ticket, + }, + }); + const { iid } = JSON.parse(remote_data.data); + + const resp = await axios.post( + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + JSON.stringify(data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + return { + data: resp.data, + message: 'Gitlab comment created', + statusCode: 201, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.comment, + ActionType.POST, + ); } - async syncComments( - linkedUserId: string, - id_ticket: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - //retrieve ticket remote id so we can retrieve the comments in the original software - const ticket = await this.prisma.tcg_tickets.findUnique({ - where: { - id_tcg_ticket: id_ticket, - }, - select: { - remote_id: true, - collections: true - }, - }); - - // retrieve the remote_id of project from collections - const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid(ticket.collections[0]) - - // Retrieve the uuid of issue from remote_data - const remote_data = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: id_ticket, - }, - }); - const { iid } = JSON.parse(remote_data.data); - - console.log("Requested URL : ", `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`) - - - const resp = await axios.get( - `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gitlab comments !`); - console.log(resp.data) - - return { - data: resp.data, - message: 'Gitlab comments retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.comment, - ActionType.GET, - ); - } + } + async syncComments( + linkedUserId: string, + id_ticket: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + //retrieve ticket remote id so we can retrieve the comments in the original software + const ticket = await this.prisma.tcg_tickets.findUnique({ + where: { + id_tcg_ticket: id_ticket, + }, + select: { + remote_id: true, + collections: true, + }, + }); + + // retrieve the remote_id of project from collections + const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid( + ticket.collections[0], + ); + + // Retrieve the uuid of issue from remote_data + const remote_data = await this.prisma.remote_data.findFirst({ + where: { + ressource_owner_id: id_ticket, + }, + }); + const { iid } = JSON.parse(remote_data.data); + + console.log( + 'Requested URL : ', + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + ); + + const resp = await axios.get( + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced gitlab comments !`); + console.log(resp.data); + + return { + data: resp.data, + message: 'Gitlab comments retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.comment, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/comment/services/gorgias/index.ts b/packages/api/src/ticketing/comment/services/gorgias/index.ts index 7b19fb25e..6f71d8d13 100644 --- a/packages/api/src/ticketing/comment/services/gorgias/index.ts +++ b/packages/api/src/ticketing/comment/services/gorgias/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { GorgiasCommentInput, GorgiasCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() @@ -53,7 +53,9 @@ export class GorgiasService implements ICommentService { }, }); if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // Assuming you want to construct the right binary attachment here // For now, we'll just return the URL @@ -94,10 +96,10 @@ export class GorgiasService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.comment, ActionType.POST, ); @@ -144,10 +146,10 @@ export class GorgiasService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/jira/index.ts b/packages/api/src/ticketing/comment/services/jira/index.ts index a0d2ed1d1..6e5fc2db0 100644 --- a/packages/api/src/ticketing/comment/services/jira/index.ts +++ b/packages/api/src/ticketing/comment/services/jira/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { JiraCommentInput, JiraCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() @@ -70,7 +70,9 @@ export class JiraService implements ICommentService { }, }); if (!attachment) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // TODO: Construct the right binary attachment // Get the AWS S3 right file @@ -112,10 +114,10 @@ export class JiraService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.comment, ActionType.POST, ); @@ -161,10 +163,10 @@ export class JiraService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/registry.service.ts b/packages/api/src/ticketing/comment/services/registry.service.ts index dc4d8d6f3..ca54192d7 100644 --- a/packages/api/src/ticketing/comment/services/registry.service.ts +++ b/packages/api/src/ticketing/comment/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ICommentService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/comment/services/zendesk/index.ts b/packages/api/src/ticketing/comment/services/zendesk/index.ts index aabcb472e..0a54d96d8 100644 --- a/packages/api/src/ticketing/comment/services/zendesk/index.ts +++ b/packages/api/src/ticketing/comment/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { OriginalCommentOutput } from '@@core/utils/types/original/original.ticketing'; @@ -62,7 +62,9 @@ export class ZendeskService implements ICommentService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO:; fetch the right file from AWS s3 const s3File = ''; @@ -112,10 +114,10 @@ export class ZendeskService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.comment, ActionType.POST, ); @@ -162,10 +164,10 @@ export class ZendeskService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/sync/sync.service.ts b/packages/api/src/ticketing/comment/sync/sync.service.ts index 98f13c20a..8777e8a2c 100644 --- a/packages/api/src/ticketing/comment/sync/sync.service.ts +++ b/packages/api/src/ticketing/comment/sync/sync.service.ts @@ -1,6 +1,6 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing comments....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -109,18 +109,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_COMMENT_SYNC_ERROR', + message: 'SyncService.syncComments() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -147,7 +154,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping comments syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -205,7 +214,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -223,7 +232,7 @@ export class SyncService implements OnModuleInit { const originId = comment.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingComment = await this.prisma.tcg_comments.findFirst({ @@ -236,15 +245,15 @@ export class SyncService implements OnModuleInit { let unique_ticketing_comment_id: string; const opts = - (comment.creator_type === 'CONTACT' && comment.contact_id) + comment.creator_type === 'CONTACT' && comment.contact_id + ? { + id_tcg_contact: comment.contact_id, + } + : comment.creator_type === 'USER' && comment.user_id ? { - id_tcg_contact: comment.contact_id, - } - : (comment.creator_type === 'USER' && comment.user_id) - ? { id_tcg_user: comment.user_id, } - : {}; + : {}; //case where nothing is passed for creator or a not authorized value; if (existingComment) { @@ -403,7 +412,7 @@ export class SyncService implements OnModuleInit { } return comments_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/contact/services/contact.service.ts b/packages/api/src/ticketing/contact/services/contact.service.ts index 345bd59ab..050b9f6d6 100644 --- a/packages/api/src/ticketing/contact/services/contact.service.ts +++ b/packages/api/src/ticketing/contact/services/contact.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedContactOutput } from '../types/model.unified'; @Injectable() @@ -74,7 +74,11 @@ export class ContactService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_CONTACT_ERROR", + message: "ContactService.getContact() call failed", + cause: error + })) } } @@ -163,7 +167,11 @@ export class ContactService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_CONTACTS_ERROR", + message: "ContactService.getContacts() call failed", + cause: error + })) } } } diff --git a/packages/api/src/ticketing/contact/services/front/index.ts b/packages/api/src/ticketing/contact/services/front/index.ts index 9b43dd9f7..928f2cc75 100644 --- a/packages/api/src/ticketing/contact/services/front/index.ts +++ b/packages/api/src/ticketing/contact/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; import { FrontContactOutput } from './types'; @@ -30,7 +30,8 @@ export class FrontService implements IContactService { remote_account_id: string, ): Promise> { try { - if (!remote_account_id) throw new Error('remote account id not found'); + if (!remote_account_id) + throw new ReferenceError('remote account id not found'); const connection = await this.prisma.connections.findFirst({ where: { @@ -59,10 +60,10 @@ export class FrontService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/services/gorgias/index.ts b/packages/api/src/ticketing/contact/services/gorgias/index.ts index b788a7d7a..07f92275c 100644 --- a/packages/api/src/ticketing/contact/services/gorgias/index.ts +++ b/packages/api/src/ticketing/contact/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; import { GorgiasContactOutput } from './types'; @@ -30,7 +30,8 @@ export class GorgiasService implements IContactService { remote_account_id: string, ): Promise> { try { - if (!remote_account_id) throw new Error('remote account id not found'); + if (!remote_account_id) + throw new ReferenceError('remote account id not found'); const connection = await this.prisma.connections.findFirst({ where: { @@ -56,10 +57,10 @@ export class GorgiasService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/services/registry.service.ts b/packages/api/src/ticketing/contact/services/registry.service.ts index be0d9c608..bc664c41c 100644 --- a/packages/api/src/ticketing/contact/services/registry.service.ts +++ b/packages/api/src/ticketing/contact/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IContactService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/contact/services/zendesk/index.ts b/packages/api/src/ticketing/contact/services/zendesk/index.ts index 6881b74e0..14fa79542 100644 --- a/packages/api/src/ticketing/contact/services/zendesk/index.ts +++ b/packages/api/src/ticketing/contact/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; @@ -59,10 +59,10 @@ export class ZendeskService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/sync/sync.service.ts b/packages/api/src/ticketing/contact/sync/sync.service.ts index 937cb48bf..870282ed4 100644 --- a/packages/api/src/ticketing/contact/sync/sync.service.ts +++ b/packages/api/src/ticketing/contact/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing contacts....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -111,18 +111,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_CONTACT_SYNC_ERROR', + message: 'SyncService.syncContacts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -149,7 +156,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -209,7 +218,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -359,7 +368,7 @@ export class SyncService implements OnModuleInit { } return contacts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/tag/services/front/index.ts b/packages/api/src/ticketing/tag/services/front/index.ts index 204a266a0..f7cd4d5c8 100644 --- a/packages/api/src/ticketing/tag/services/front/index.ts +++ b/packages/api/src/ticketing/tag/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { FrontTagOutput } from './types'; @@ -66,10 +66,10 @@ export class FrontService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/gorgias/index.ts b/packages/api/src/ticketing/tag/services/gorgias/index.ts index 8e476f405..fcccc8166 100644 --- a/packages/api/src/ticketing/tag/services/gorgias/index.ts +++ b/packages/api/src/ticketing/tag/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { GorgiasTagOutput } from './types'; @@ -66,10 +66,10 @@ export class GorgiasService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/jira/index.ts b/packages/api/src/ticketing/tag/services/jira/index.ts index 00c3ef3f5..7096dd994 100644 --- a/packages/api/src/ticketing/tag/services/jira/index.ts +++ b/packages/api/src/ticketing/tag/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { JiraTagOutput } from './types'; @@ -67,10 +67,10 @@ export class JiraService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/tag.service.ts b/packages/api/src/ticketing/tag/services/tag.service.ts index e30eea4e6..b824b467c 100644 --- a/packages/api/src/ticketing/tag/services/tag.service.ts +++ b/packages/api/src/ticketing/tag/services/tag.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedTagOutput } from '../types/model.unified'; @Injectable() @@ -71,7 +71,11 @@ export class TagService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_TAG_ERROR", + message: "TagService.getTag() call failed", + cause: error + })) } } @@ -157,7 +161,11 @@ export class TagService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_TAGS_ERROR", + message: "TagService.getTags() call failed", + cause: error + })) } } } diff --git a/packages/api/src/ticketing/tag/services/zendesk/index.ts b/packages/api/src/ticketing/tag/services/zendesk/index.ts index 86c2d604b..7a5a5ab23 100644 --- a/packages/api/src/ticketing/tag/services/zendesk/index.ts +++ b/packages/api/src/ticketing/tag/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; @@ -67,10 +67,10 @@ export class ZendeskService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/sync/sync.service.ts b/packages/api/src/ticketing/tag/sync/sync.service.ts index be264f495..5dbb08c47 100644 --- a/packages/api/src/ticketing/tag/sync/sync.service.ts +++ b/packages/api/src/ticketing/tag/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing tags....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -109,18 +109,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TAG_SYNC_ERROR', + message: 'SyncService.syncTags() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -147,7 +154,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tags syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -183,7 +192,6 @@ export class SyncService implements OnModuleInit { //TODO: exceptionally we use the unifiedObject as we might need to get the fake remote ids from Zendesk store in id field - //insert the data in the DB with the fieldMappings (value table) const tag_data = await this.saveTagsInDb( linkedUserId, @@ -212,7 +220,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -336,7 +344,7 @@ export class SyncService implements OnModuleInit { } return tags_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/team/services/front/index.ts b/packages/api/src/ticketing/team/services/front/index.ts index d2e38be05..13583f0d1 100644 --- a/packages/api/src/ticketing/team/services/front/index.ts +++ b/packages/api/src/ticketing/team/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { FrontTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/gorgias/index.ts b/packages/api/src/ticketing/team/services/gorgias/index.ts index a33a1cd29..466c332d7 100644 --- a/packages/api/src/ticketing/team/services/gorgias/index.ts +++ b/packages/api/src/ticketing/team/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { GorgiasTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class GorgiasService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/jira/index.ts b/packages/api/src/ticketing/team/services/jira/index.ts index 1a0cad3c0..12aa7c627 100644 --- a/packages/api/src/ticketing/team/services/jira/index.ts +++ b/packages/api/src/ticketing/team/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { JiraTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class JiraService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/team.service.ts b/packages/api/src/ticketing/team/services/team.service.ts index ee1112b38..da8e8f53f 100644 --- a/packages/api/src/ticketing/team/services/team.service.ts +++ b/packages/api/src/ticketing/team/services/team.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedTeamOutput } from '../types/model.unified'; @Injectable() @@ -72,7 +72,11 @@ export class TeamService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_TEAM_ERROR", + message: "TeamService.getTeam() call failed", + cause: error + })) } } @@ -160,7 +164,11 @@ export class TeamService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_TEAMS_ERROR", + message: "TeamService.getTeams() call failed", + cause: error + })) } } } diff --git a/packages/api/src/ticketing/team/services/zendesk/index.ts b/packages/api/src/ticketing/team/services/zendesk/index.ts index 03d72febf..6bb731cb9 100644 --- a/packages/api/src/ticketing/team/services/zendesk/index.ts +++ b/packages/api/src/ticketing/team/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; @@ -55,10 +55,10 @@ export class ZendeskService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/sync/sync.service.ts b/packages/api/src/ticketing/team/sync/sync.service.ts index 024da3505..7a6413ec2 100644 --- a/packages/api/src/ticketing/team/sync/sync.service.ts +++ b/packages/api/src/ticketing/team/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalTeamOutput } from '@@core/utils/types/original/original.ticketi import { TICKETING_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing teams....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TEAM_SYNC_ERROR', + message: 'SyncService.syncTeams() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping teams syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +205,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +222,7 @@ export class SyncService implements OnModuleInit { const originId = team.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTeam = await this.prisma.tcg_teams.findFirst({ @@ -319,7 +328,7 @@ export class SyncService implements OnModuleInit { } return teams_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/ticket/services/front/index.ts b/packages/api/src/ticketing/ticket/services/front/index.ts index 5e05808a8..5c472abca 100644 --- a/packages/api/src/ticketing/ticket/services/front/index.ts +++ b/packages/api/src/ticketing/ticket/services/front/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { FrontTicketInput, FrontTicketOutput } from './types'; import { Utils } from '@ticketing/@lib/@utils'; @@ -54,7 +54,9 @@ export class FrontService implements ITicketService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO: construct the right binary attachment //get the AWS s3 right file //TODO: check how to send a stream of a url @@ -152,10 +154,10 @@ export class FrontService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.ticket, ActionType.POST, ); @@ -189,10 +191,10 @@ export class FrontService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/github/index.ts b/packages/api/src/ticketing/ticket/services/github/index.ts index 4437a2f42..f703b293c 100644 --- a/packages/api/src/ticketing/ticket/services/github/index.ts +++ b/packages/api/src/ticketing/ticket/services/github/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GithubTicketInput, GithubTicketOutput } from './types'; @@ -57,10 +57,10 @@ export class GithubService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Github', + 'github', TicketingObject.ticket, ActionType.POST, ); @@ -96,10 +96,10 @@ export class GithubService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Github', + 'github', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/gitlab/index.ts b/packages/api/src/ticketing/ticket/services/gitlab/index.ts index b3552c9df..c3adcb056 100644 --- a/packages/api/src/ticketing/ticket/services/gitlab/index.ts +++ b/packages/api/src/ticketing/ticket/services/gitlab/index.ts @@ -6,101 +6,103 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GitlabTicketInput, GitlabTicketOutput } from './types'; - @Injectable() export class GitlabService implements ITicketService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.ticket.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } - async addTicket( - ticketData: GitlabTicketInput, - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - const dataBody = ticketData; + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.ticket.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } + async addTicket( + ticketData: GitlabTicketInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + const dataBody = ticketData; - const resp = await axios.post( - `${connection.account_url}/projects/${ticketData.project_id}/issues`, - JSON.stringify(dataBody), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - return { - data: resp.data, - message: 'Gitlab ticket created', - statusCode: 201, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.ticket, - ActionType.POST, - ); - } + const resp = await axios.post( + `${connection.account_url}/projects/${ticketData.project_id}/issues`, + JSON.stringify(dataBody), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data, + message: 'Gitlab ticket created', + statusCode: 201, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.ticket, + ActionType.POST, + ); } - async syncTickets( - linkedUserId: string, - custom_properties?: string[], - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + } + async syncTickets( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const resp = await axios.get(`${connection.account_url}/issues?scope=created_by_me&scope=assigned_to_me`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - this.logger.log(`Synced gitlab tickets !`); + const resp = await axios.get( + `${connection.account_url}/issues?scope=created_by_me&scope=assigned_to_me`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced gitlab tickets !`); - return { - data: resp.data, - message: 'Gitlab tickets retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.ticket, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'Gitlab tickets retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.ticket, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/ticket/services/gorgias/index.ts b/packages/api/src/ticketing/ticket/services/gorgias/index.ts index 993b8412e..7c05ee1f0 100644 --- a/packages/api/src/ticketing/ticket/services/gorgias/index.ts +++ b/packages/api/src/ticketing/ticket/services/gorgias/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GorgiasTicketInput, GorgiasTicketOutput } from './types'; import * as fs from 'fs'; @@ -51,7 +51,9 @@ export class GorgiasService implements ITicketService { }, }); if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // Assuming you want to construct the right binary attachment here // For now, we'll just return the URL @@ -94,10 +96,10 @@ export class GorgiasService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.ticket, ActionType.POST, ); @@ -132,10 +134,10 @@ export class GorgiasService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/hubspot/index.ts b/packages/api/src/ticketing/ticket/services/hubspot/index.ts index a4c408319..abf70bc94 100644 --- a/packages/api/src/ticketing/ticket/services/hubspot/index.ts +++ b/packages/api/src/ticketing/ticket/services/hubspot/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { HubspotTicketInput, @@ -58,10 +58,10 @@ export class HubspotService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Hubspot', + 'hubspot', TicketingObject.ticket, ActionType.POST, ); @@ -105,10 +105,10 @@ export class HubspotService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Hubspot', + 'hubspot', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/jira/index.ts b/packages/api/src/ticketing/ticket/services/jira/index.ts index 597bf3673..8d35a7b56 100644 --- a/packages/api/src/ticketing/ticket/services/jira/index.ts +++ b/packages/api/src/ticketing/ticket/services/jira/index.ts @@ -6,10 +6,10 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { JiraTicketInput, JiraTicketOutput } from './types'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; @Injectable() export class JiraService implements ITicketService { @@ -64,10 +64,10 @@ export class JiraService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.ticket, ActionType.POST, ); @@ -100,10 +100,10 @@ export class JiraService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/jira/mappers.ts b/packages/api/src/ticketing/ticket/services/jira/mappers.ts index 25716d262..a577cd5c5 100644 --- a/packages/api/src/ticketing/ticket/services/jira/mappers.ts +++ b/packages/api/src/ticketing/ticket/services/jira/mappers.ts @@ -21,7 +21,9 @@ export class JiraTicketMapper implements ITicketMapper { }[], ): Promise { if (!source.project_id) { - throw new Error('a project key/id is mandatory for Jira ticket creation'); + throw new ReferenceError( + 'a project key/id is mandatory for Jira ticket creation', + ); } const result: JiraTicketInput = { fields: { diff --git a/packages/api/src/ticketing/ticket/services/registry.service.ts b/packages/api/src/ticketing/ticket/services/registry.service.ts index bde87a420..9f2f6944b 100644 --- a/packages/api/src/ticketing/ticket/services/registry.service.ts +++ b/packages/api/src/ticketing/ticket/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ITicketService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/ticket/services/ticket.service.ts b/packages/api/src/ticketing/ticket/services/ticket.service.ts index 459476de7..2f9fdf302 100644 --- a/packages/api/src/ticketing/ticket/services/ticket.service.ts +++ b/packages/api/src/ticketing/ticket/services/ticket.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedTicketInput, @@ -16,6 +15,7 @@ import { FieldMappingService } from '@@core/field-mapping/field-mapping.service' import { unify } from '@@core/utils/unification/unify'; import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; @Injectable() export class TicketService { @@ -48,7 +48,13 @@ export class TicketService { ); return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_TICKETS_ERROR', + message: 'TicketService.batchAddTickets() call failed', + cause: error, + }), + ); } } @@ -65,7 +71,7 @@ export class TicketService { }, }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const acc = unifiedTicketData.account_id; //check if contact_id and account_id refer to real uuids if (acc) { @@ -75,7 +81,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted an account_id which does not exist'); + throw new ReferenceError( + 'You inserted an account_id which does not exist', + ); } const contact = unifiedTicketData.contact_id; @@ -87,7 +95,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const assignees = unifiedTicketData.assigned_to; //CHEK IF assigned_to contains valid Users uuids @@ -99,7 +109,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted an assignee which does not exist'); + throw new ReferenceError( + 'You inserted an assignee which does not exist', + ); }); } // Retrieve custom field mappings @@ -329,7 +341,13 @@ export class TicketService { ); return result_ticket; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_TICKET_ERROR', + message: 'TicketService.addTicket() call failed', + cause: error, + }), + ); } } @@ -405,7 +423,13 @@ export class TicketService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TICKET_ERROR', + message: 'TicketService.getTicket() call failed', + cause: error, + }), + ); } } @@ -508,7 +532,13 @@ export class TicketService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TICKETS_ERROR', + message: 'TicketService.getTickets() call failed', + cause: error, + }), + ); } } //TODO @@ -517,9 +547,7 @@ export class TicketService { updateTicketData: Partial, ): Promise { try { - } catch (error) { - handleServiceError(error, this.logger); - } + } catch (error) {} // TODO: fetch the ticket from the database using 'id' // TODO: update the ticket with 'updateTicketData' // TODO: save the updated ticket back to the database diff --git a/packages/api/src/ticketing/ticket/services/zendesk/index.ts b/packages/api/src/ticketing/ticket/services/zendesk/index.ts index 4e1d93f04..ccf57173f 100644 --- a/packages/api/src/ticketing/ticket/services/zendesk/index.ts +++ b/packages/api/src/ticketing/ticket/services/zendesk/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ZendeskTicketInput, ZendeskTicketOutput } from './types'; @@ -52,7 +52,9 @@ export class ZendeskService implements ITicketService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO:; fetch the right file from AWS s3 const s3File = ''; @@ -103,10 +105,10 @@ export class ZendeskService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.ticket, ActionType.POST, ); @@ -141,10 +143,10 @@ export class ZendeskService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/sync/sync.service.ts b/packages/api/src/ticketing/ticket/sync/sync.service.ts index 815e914c7..0b346f918 100644 --- a/packages/api/src/ticketing/ticket/sync/sync.service.ts +++ b/packages/api/src/ticketing/ticket/sync/sync.service.ts @@ -1,6 +1,6 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TICKET_SYNC_ERROR', + message: 'SyncService.syncTickets() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tickets syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -194,7 +203,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -211,7 +220,7 @@ export class SyncService implements OnModuleInit { const originId = ticket.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTicket = await this.prisma.tcg_tickets.findFirst({ @@ -373,7 +382,7 @@ export class SyncService implements OnModuleInit { } return tickets_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/user/services/front/index.ts b/packages/api/src/ticketing/user/services/front/index.ts index b7f59f8bf..2bdd8c716 100644 --- a/packages/api/src/ticketing/user/services/front/index.ts +++ b/packages/api/src/ticketing/user/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { FrontUserOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/gitlab/index.ts b/packages/api/src/ticketing/user/services/gitlab/index.ts index 15d1ff254..4ae3af237 100644 --- a/packages/api/src/ticketing/user/services/gitlab/index.ts +++ b/packages/api/src/ticketing/user/services/gitlab/index.ts @@ -5,62 +5,62 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { GitlabUserOutput } from './types'; @Injectable() export class GitlabService implements IUserService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.user.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.user.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } - async syncUsers( - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + async syncUsers( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const resp = await axios.get(`${connection.account_url}/users`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - this.logger.log(`Synced gitlab users !`); + const resp = await axios.get(`${connection.account_url}/users`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced gitlab users !`); - // console.log("Users Data : ", resp.data); + // console.log("Users Data : ", resp.data); - return { - data: resp.data, - message: 'gitlab users retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.user, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'gitlab users retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.user, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/user/services/gorgias/index.ts b/packages/api/src/ticketing/user/services/gorgias/index.ts index b04e7ae29..42c2207c5 100644 --- a/packages/api/src/ticketing/user/services/gorgias/index.ts +++ b/packages/api/src/ticketing/user/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { GorgiasUserOutput } from './types'; @@ -52,10 +52,10 @@ export class GorgiasService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/jira/index.ts b/packages/api/src/ticketing/user/services/jira/index.ts index 61ac6baae..7dd0e687e 100644 --- a/packages/api/src/ticketing/user/services/jira/index.ts +++ b/packages/api/src/ticketing/user/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { JiraUserOutput } from './types'; @@ -51,10 +51,10 @@ export class JiraService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/user.service.ts b/packages/api/src/ticketing/user/services/user.service.ts index 329643462..8718e4ece 100644 --- a/packages/api/src/ticketing/user/services/user.service.ts +++ b/packages/api/src/ticketing/user/services/user.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedUserOutput } from '../types/model.unified'; @Injectable() @@ -22,6 +22,8 @@ export class UserService { }, }); + if(!user) throw new ReferenceError('User undefined') + // Fetch field mappings for the ticket const values = await this.prisma.value.findMany({ where: { @@ -73,7 +75,11 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_USER_ERROR", + message: "UserService.getUser() call failed", + cause: error + })) } } @@ -162,7 +168,11 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new UnifiedTicketingError({ + name: "GET_USERS_ERROR", + message: "UserService.getUsers() call failed", + cause: error + })) } } } diff --git a/packages/api/src/ticketing/user/services/zendesk/index.ts b/packages/api/src/ticketing/user/services/zendesk/index.ts index 8a855a9fb..198330c86 100644 --- a/packages/api/src/ticketing/user/services/zendesk/index.ts +++ b/packages/api/src/ticketing/user/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; @@ -57,10 +57,10 @@ export class ZendeskService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/sync/sync.service.ts b/packages/api/src/ticketing/user/sync/sync.service.ts index 02d79089e..c3fa8ed75 100644 --- a/packages/api/src/ticketing/user/sync/sync.service.ts +++ b/packages/api/src/ticketing/user/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { UnifiedUserOutput } from '../types/model.unified'; import { TICKETING_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,12 +35,12 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } private async scheduleSyncJob() { - try{ + try { const jobName = 'ticketing-sync-users'; // Remove existing jobs to avoid duplicates in case of application restart @@ -58,8 +58,8 @@ export class SyncService implements OnModuleInit { repeat: { cron: '0 0 * * *' }, // Runs once a day at midnight }, ); - }catch(error){ - throw new Error(error); + } catch (error) { + throw error; } } //function used by sync worker which populate our tcg_users table @@ -71,12 +71,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing users....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -103,18 +103,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_USER_SYNC_ERROR', + message: 'SyncService.syncUsers() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -140,7 +147,9 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + throw ReferenceError( + `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, + ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -172,8 +181,6 @@ export class SyncService implements OnModuleInit { customFieldMappings, })) as UnifiedUserOutput[]; - - //insert the data in the DB with the fieldMappings (value table) const user_data = await this.saveUsersInDb( linkedUserId, @@ -201,7 +208,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -330,7 +337,7 @@ export class SyncService implements OnModuleInit { } return users_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/user/user.controller.ts b/packages/api/src/ticketing/user/user.controller.ts index ea4c4132c..7df506a30 100644 --- a/packages/api/src/ticketing/user/user.controller.ts +++ b/packages/api/src/ticketing/user/user.controller.ts @@ -56,15 +56,11 @@ export class UserController { @Headers('x-connection-token') connection_token: string, @Query('remote_data') remote_data?: boolean, ) { - try { - const { linkedUserId, remoteSource } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.userService.getUsers(remoteSource, linkedUserId, remote_data); - } catch (error) { - throw new Error(error); - } + const { linkedUserId, remoteSource } = + await this.connectionUtils.getConnectionMetadataFromConnectionToken( + connection_token, + ); + return this.userService.getUsers(remoteSource, linkedUserId, remote_data); } @ApiOperation({ diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index 98c1cf83d..d55527f27 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -21,7 +21,7 @@ export const constructAuthUrl = async ({ projectId, linkedUserId, providerName, // console.log('encodedRedirect URL : ', encodedRedirectUrl); // const vertical = findConnectorCategory(providerName); if (vertical == null) { - throw new Error('vertical is null'); + throw new ReferenceError('vertical is null'); } const config = CONNECTORS_METADATA[vertical.toLowerCase()][providerName]; @@ -85,13 +85,13 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { // console.log("Fetched Data ", JSON.stringify(data)) const clientId = data.CLIENT_ID; - if (!clientId) throw new Error(`No client id for type ${type}`) + if (!clientId) throw new ReferenceError(`No client id for type ${type}`) const scopes = data.SCOPE const { urls: urls } = config; const { authBaseUrl: baseUrl } = urls; - if (!baseUrl) throw new Error(`No authBaseUrl found for type ${type}`) + if (!baseUrl) throw new ReferenceError(`No authBaseUrl found for type ${type}`) // construct the baseAuthUrl based on the fact that client may use custom subdomain const BASE_URL: string = providerName === 'gorgias' ? `${apiUrl}${baseUrl}` : diff --git a/packages/shared/src/envConfig.ts b/packages/shared/src/envConfig.ts index 4ba8821d6..3d58a5c1b 100644 --- a/packages/shared/src/envConfig.ts +++ b/packages/shared/src/envConfig.ts @@ -72,7 +72,7 @@ export function extractAuthMode(type: string): AuthStrategy { case 'BASIC': return AuthStrategy.basic; default: - throw new Error('Auth mode not found'); + throw new ReferenceError('Auth mode not found'); } } From 0fb4b0ef7334cc3fa1fc55dc30651b80a801e32a Mon Sep 17 00:00:00 2001 From: nael Date: Thu, 13 Jun 2024 21:10:43 +0200 Subject: [PATCH 5/9] :bug: Fix notfound errors --- packages/api/prisma/schema.prisma | 1 + .../services/accounting.connection.service.ts | 38 +++++++++------- .../crm/services/crm.connection.service.ts | 40 +++++++++-------- .../crm/services/zoho/zoho.service.ts | 44 +++++++++++-------- .../marketingautomation.connection.service.ts | 38 +++++++++------- .../services/ticketing.connection.service.ts | 40 +++++++++-------- .../ticketing/contact/sync/sync.service.ts | 2 +- .../src/ticketing/user/sync/sync.service.ts | 2 +- 8 files changed, 115 insertions(+), 90 deletions(-) diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 82eef4c9f..c957f3681 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -109,6 +109,7 @@ model connections { linked_users linked_users @relation(fields: [id_linked_user], references: [id_linked_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_11") projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_9") + @@unique([access_token, refresh_token], map: "index_3") @@index([id_project], map: "fk_1") @@index([id_linked_user], map: "fk_connections_to_linkedusersid") } diff --git a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts index b2b7b1d57..9a595fc58 100644 --- a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts +++ b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -43,7 +43,7 @@ export class AccountingConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -73,13 +73,15 @@ export class AccountingConnectionsService { event.id_event, ); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", - message: "AccountingConnectionsService.handleAccountngCallback() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_ACCOUNTING', + message: + 'AccountingConnectionsService.handleAccountngCallback() call failed', + cause: error, + }), + this.logger, + ); } } @@ -94,7 +96,7 @@ export class AccountingConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -104,13 +106,15 @@ export class AccountingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", - message: "AccountingConnectionsService.handleAccountingTokensRefresh() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_ACCOUNTING', + message: + 'AccountingConnectionsService.handleAccountingTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts index 0add79829..2662ab102 100644 --- a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts +++ b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -42,7 +42,9 @@ export class CrmConnectionsService { ) { try { if (!code) { - throw new NotFoundError(`no ${providerName} code found, found ${code}`); + throw new ReferenceError( + `no ${providerName} code found, found ${code}`, + ); } const serviceName = providerName.toLowerCase(); @@ -50,7 +52,7 @@ export class CrmConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -81,13 +83,14 @@ export class CrmConnectionsService { event.id_event, ); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_CRM", - message: "CrmConnectionsService.handleCRMCallBack() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', + message: 'CrmConnectionsService.handleCRMCallBack() call failed', + cause: error, + }), + this.logger, + ); } } @@ -102,7 +105,7 @@ export class CrmConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -112,13 +115,14 @@ export class CrmConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_CRM", - message: "CrmConnectionsService.handleCRMTokensRefresh() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', + message: 'CrmConnectionsService.handleCRMTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts index b3d30c43f..266b3c1c7 100644 --- a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts +++ b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts @@ -7,7 +7,13 @@ import { RefreshParams, } from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; -import { Action, ActionType, ConnectionsError, NotFoundError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; @@ -80,7 +86,7 @@ export class ZohoConnectionService implements ICrmConnectionService { try { const { linkedUserId, projectId, code, location } = opts; if (!location) { - throw new NotFoundError(`no zoho location, found ${location}`); + throw new ReferenceError(`no zoho location, found ${location}`); } const isNotUnique = await this.prisma.connections.findFirst({ where: { @@ -176,17 +182,18 @@ export class ZohoConnectionService implements ICrmConnectionService { return db_res; } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_CRM", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', message: `ZohoConnectionService.handleCallback() call failed ---> ${format3rdPartyError( - "zoho", + 'zoho', Action.oauthCallback, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } async handleTokenRefresh(opts: RefreshParams) { @@ -228,17 +235,18 @@ export class ZohoConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : zoho '); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_CRM", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', message: `ZohoConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( - "zoho", + 'zoho', Action.oauthRefresh, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts index d32ebb0c4..6799f5d8f 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -43,7 +43,7 @@ export class MarketingAutomationConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -73,13 +73,15 @@ export class MarketingAutomationConnectionsService { event.id_event, ); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", - message: "MarketingAutomationConnectionsService.handleMarketingAutomationCallBack() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION', + message: + 'MarketingAutomationConnectionsService.handleMarketingAutomationCallBack() call failed', + cause: error, + }), + this.logger, + ); } } @@ -94,7 +96,7 @@ export class MarketingAutomationConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -104,13 +106,15 @@ export class MarketingAutomationConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", - message: "MarketingAutomationConnectionsService.handleMarketingAutomationTokensRefresh() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION', + message: + 'MarketingAutomationConnectionsService.handleMarketingAutomationTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts index 5c39cbc21..8f1389e95 100644 --- a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ConnectionsError, NotFoundError, throwTypedError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -44,7 +44,7 @@ export class TicketingConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -75,15 +75,17 @@ export class TicketingConnectionsService { event.id_event, ); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_TICKETING", - message: "TicketingConnectionsService.handleTicketingCallBack() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_TICKETING', + message: + 'TicketingConnectionsService.handleTicketingCallBack() call failed', + cause: error, + }), + this.logger, + ); } - } + } async handleTicketingTokensRefresh( connectionId: string, @@ -96,7 +98,7 @@ export class TicketingConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -106,13 +108,15 @@ export class TicketingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_TICKETING", - message: "TicketingConnectionsService.handleTicketingTokensRefresh() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_TICKETING', + message: + 'TicketingConnectionsService.handleTicketingTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/ticketing/contact/sync/sync.service.ts b/packages/api/src/ticketing/contact/sync/sync.service.ts index 870282ed4..f2f35176c 100644 --- a/packages/api/src/ticketing/contact/sync/sync.service.ts +++ b/packages/api/src/ticketing/contact/sync/sync.service.ts @@ -236,7 +236,7 @@ export class SyncService implements OnModuleInit { const originId = contact.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingContact = await this.prisma.tcg_contacts.findFirst({ diff --git a/packages/api/src/ticketing/user/sync/sync.service.ts b/packages/api/src/ticketing/user/sync/sync.service.ts index c3fa8ed75..594cf4f43 100644 --- a/packages/api/src/ticketing/user/sync/sync.service.ts +++ b/packages/api/src/ticketing/user/sync/sync.service.ts @@ -225,7 +225,7 @@ export class SyncService implements OnModuleInit { const originId = user.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingUser = await this.prisma.tcg_users.findFirst({ From b7dbbb4a8fd8f2beb0593c5f5da2cc0db9d0db69 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 14 Jun 2024 01:12:05 +0200 Subject: [PATCH 6/9] :bug: Fixings --- docs/api-reference/pagination.mdx | 2 +- packages/api/prisma/schema.prisma | 2 +- packages/api/scripts/connectorUpdate.js | 84 +++++++++ packages/api/scripts/init.sql | 2 +- packages/api/scripts/seed.sql | 8 +- .../connections-strategies.service.ts | 173 ++++++++++-------- .../connections/connections.controller.ts | 21 ++- .../ticketing/services/jira/jira.service.ts | 62 +++---- .../project-connectors.controller.ts | 18 +- .../project-connectors.service.ts | 6 +- packages/api/src/@core/sync/sync.service.ts | 1 - .../api/src/@core/webhook/webhook.service.ts | 21 +-- .../api/src/crm/company/sync/sync.service.ts | 4 +- .../api/src/crm/contact/sync/sync.service.ts | 4 +- .../api/src/crm/deal/sync/sync.service.ts | 4 +- .../src/crm/engagement/sync/sync.service.ts | 4 +- .../api/src/crm/note/sync/sync.service.ts | 4 +- .../api/src/crm/stage/sync/sync.service.ts | 4 +- .../api/src/crm/task/sync/sync.service.ts | 4 +- .../api/src/crm/user/sync/sync.service.ts | 4 +- .../ticketing/account/sync/sync.service.ts | 3 - .../ticketing/collection/sync/sync.service.ts | 3 - .../ticketing/comment/sync/sync.service.ts | 3 - .../ticketing/contact/sync/sync.service.ts | 3 - .../src/ticketing/tag/sync/sync.service.ts | 3 - .../src/ticketing/team/sync/sync.service.ts | 3 - .../src/ticketing/ticket/sync/sync.service.ts | 3 - .../src/ticketing/user/sync/sync.service.ts | 3 - packages/api/swagger/swagger-spec.json | 14 -- 29 files changed, 250 insertions(+), 220 deletions(-) diff --git a/docs/api-reference/pagination.mdx b/docs/api-reference/pagination.mdx index b1baace0d..82b8a711c 100644 --- a/docs/api-reference/pagination.mdx +++ b/docs/api-reference/pagination.mdx @@ -2,4 +2,4 @@ title: "Pagination" description: "" --- -Panora’s list API methods use cursor-based pagination through the `starting_after` and `ending_before` parameters. Both parameters accept an existing object ID value (see below) and return objects in reverse chronological order. The `ending_before` parameter returns objects listed before the named object. The `starting_after` parameter returns objects listed after the named object. These parameters are mutually exclusive. You can use either the `starting_after` or `ending_before` parameter, but not both simultaneously. \ No newline at end of file +Panora’s list API methods use cursor-based pagination through the `pageSize` and `cursor` parameters. diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index c957f3681..7e528d24a 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -109,7 +109,6 @@ model connections { linked_users linked_users @relation(fields: [id_linked_user], references: [id_linked_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_11") projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_9") - @@unique([access_token, refresh_token], map: "index_3") @@index([id_project], map: "fk_1") @@index([id_linked_user], map: "fk_connections_to_linkedusersid") } @@ -650,6 +649,7 @@ model connector_sets { crm_zoho Boolean crm_attio Boolean crm_pipedrive Boolean + crm_zendesk Boolean crm_close Boolean tcg_zendesk Boolean tcg_jira Boolean diff --git a/packages/api/scripts/connectorUpdate.js b/packages/api/scripts/connectorUpdate.js index 30adf11fc..f2582b431 100755 --- a/packages/api/scripts/connectorUpdate.js +++ b/packages/api/scripts/connectorUpdate.js @@ -12,6 +12,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +import { slugFromCategory } from '@panora/shared'; // Function to scan the directory for new service directories function scanDirectory(dir) { @@ -332,6 +333,82 @@ function updateEnumFile(enumFilePath, newServiceDirs, vertical) { console.error(`Could not find enum ${enumName} in file.`); } } + +// New function to update init.sql +function updateInitSQLFile(initSQLFile, newServiceDirs, vertical) { + let fileContent = fs.readFileSync(initSQLFile, 'utf8'); + const insertPoint = fileContent.indexOf( + 'CONSTRAINT PK_project_connector PRIMARY KEY', + ); + + if (insertPoint === -1) { + console.error( + `Could not find the PRIMARY KEY constraint in ${initSQLFile}`, + ); + return; + } + + let newLines = ''; + newServiceDirs.forEach((serviceName) => { + const columnName = `${vertical.toLowerCase()}_${serviceName.toLowerCase()}`; + newLines += ` ${columnName} boolean NOT NULL,\n`; + }); + + fileContent = [ + fileContent.slice(0, insertPoint), + newLines, + fileContent.slice(insertPoint), + ].join(''); + + fs.writeFileSync(initSQLFile, fileContent); +} + +// New function to update seed.sql +function updateSeedSQLFile(seedSQLFile, newServiceDirs, vertical) { + let fileContent = fs.readFileSync(seedSQLFile, 'utf8'); + + const tableInsertPoint = fileContent.indexOf('INSERT INTO connector_sets'); + if (tableInsertPoint === -1) { + console.error( + `Could not find the INSERT INTO connector_sets statement in ${seedSQLFile}`, + ); + return; + } + + const columnInsertPoint = fileContent.indexOf('(', tableInsertPoint); + const valuesInsertPoint = fileContent.indexOf('VALUES', columnInsertPoint); + const rowsInsertPoint = fileContent.indexOf('(', valuesInsertPoint); + + if ( + columnInsertPoint === -1 || + valuesInsertPoint === -1 || + rowsInsertPoint === -1 + ) { + console.error( + `Could not find the column or values insert points in ${seedSQLFile}`, + ); + return; + } + + let newColumns = ''; + let newValues = ''; + newServiceDirs.forEach((serviceName) => { + const columnName = `${vertical.toLowerCase()}_${serviceName.toLowerCase()}`; + newColumns += `${columnName}, `; + newValues += 'TRUE, '; + }); + + const updatedFileContent = [ + fileContent.slice(0, columnInsertPoint + 1), + newColumns, + fileContent.slice(columnInsertPoint + 1, rowsInsertPoint + 1), + newValues, + fileContent.slice(rowsInsertPoint + 1), + ].join(''); + + fs.writeFileSync(seedSQLFile, updatedFileContent); +} + // Main script logic function updateObjectTypes(baseDir, objectType, vertical) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -406,6 +483,13 @@ function updateObjectTypes(baseDir, objectType, vertical) { possibleProviderForImportStatements, objectType, ); + + // Update SQL files + const initSQLFile = path.join(__dirname, './init.sql'); + updateInitSQLFile(initSQLFile, newServiceDirs, slugFromCategory(vertical)); + + const seedSQLFile = path.join(__dirname, './seed.sql'); + updateSeedSQLFile(seedSQLFile, newServiceDirs, slugFromCategory(vertical)); } // Example usage for ticketing/team diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 09ed47f21..dcac88701 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -394,6 +394,7 @@ CREATE TABLE connector_sets crm_zoho boolean NOT NULL, crm_attio boolean NOT NULL, crm_pipedrive boolean NOT NULL, + crm_zendesk boolean NOT NULL, crm_close boolean NOT NULL, tcg_zendesk boolean NOT NULL, tcg_jira boolean NOT NULL, @@ -1301,7 +1302,6 @@ CREATE TABLE connections id_project uuid NOT NULL, id_linked_user uuid NOT NULL, CONSTRAINT PK_connections PRIMARY KEY ( id_connection ), - CONSTRAINT Index_3 UNIQUE ( access_token, refresh_token ), CONSTRAINT FK_9 FOREIGN KEY ( id_project ) REFERENCES projects ( id_project ), CONSTRAINT FK_11 FOREIGN KEY ( id_linked_user ) REFERENCES linked_users ( id_linked_user ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 1dddd2fc4..620985342 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pool', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts index 3fe1c5803..94a905c59 100644 --- a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts +++ b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts @@ -1,7 +1,10 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ConnectionStrategiesError, throwTypedError } from '@@core/utils/errors'; +import { + ConnectionStrategiesError, + throwTypedError, +} from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { @@ -36,7 +39,7 @@ export class ConnectionsStrategiesService { ) {} async isCustomCredentials(projectId: string, type: string) { - try{ + try { const res = await this.prisma.connection_strategies.findFirst({ where: { id_project: projectId, @@ -44,18 +47,19 @@ export class ConnectionsStrategiesService { status: true, }, }); - if (!res) throw ReferenceError('Connection strategy undefined') + if (!res) return false; return res.status; - }catch(error){ - throwTypedError(new ConnectionStrategiesError( - { - name: "CUSTOM_CREDENTIALS_ERROR", - message: "ConnectionsStrategiesService.isCustomCredentials() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'CUSTOM_CREDENTIALS_ERROR', + message: + 'ConnectionsStrategiesService.isCustomCredentials() call failed', + cause: error, + }), + this.logger, + ); } - } async createConnectionStrategy( @@ -64,19 +68,20 @@ export class ConnectionsStrategiesService { attributes: string[], values: string[], ) { - try{ - const checkCSDuplicate = await this.prisma.connection_strategies.findFirst({ - where: { - id_project: projectId, - type: type, - }, - }); + try { + const checkCSDuplicate = + await this.prisma.connection_strategies.findFirst({ + where: { + id_project: projectId, + type: type, + }, + }); if (checkCSDuplicate) throw new ConnectionStrategiesError({ - name: "CONNECTION_STRATEGY_ALREADY_EXISTS", - message: `Connection strategy already exists for projectId=${projectId} and type=${type}` + name: 'CONNECTION_STRATEGY_ALREADY_EXISTS', + message: `Connection strategy already exists for projectId=${projectId} and type=${type}`, }); - + const cs = await this.prisma.connection_strategies.create({ data: { id_connection_strategy: uuidv4(), @@ -111,16 +116,18 @@ export class ConnectionsStrategiesService { }, }); } - + return cs; - }catch(error){ - throwTypedError(new ConnectionStrategiesError( - { - name: "CREATE_CONNECTION_STRATEGY_ERROR", - message: "ConnectionsStrategiesService.createConnectionStrategy() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'CREATE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.createConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); } } @@ -144,13 +151,14 @@ export class ConnectionsStrategiesService { return updatedCs; } catch (error) { - throwTypedError(new ConnectionStrategiesError( - { - name: "TOGGLE_CONNECTION_STRATEGY_ERROR", - message: "ConnectionsStrategiesService.toggle() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionStrategiesError({ + name: 'TOGGLE_CONNECTION_STRATEGY_ERROR', + message: 'ConnectionsStrategiesService.toggle() call failed', + cause: error, + }), + this.logger, + ); } } @@ -173,7 +181,8 @@ export class ConnectionsStrategiesService { id_connection_strategy: cs.id_connection_strategy, }, }); - if (!entity) throw new ReferenceError('Connection strategy entity undefined !'); + if (!entity) + throw new ReferenceError('Connection strategy entity undefined !'); const authValues: string[] = []; for (let i = 0; i < attributes.length; i++) { @@ -185,13 +194,15 @@ export class ConnectionsStrategiesService { attribute_slug: attribute_slug, }, }); - if (!attribute_) throw new ReferenceError('Connection Strategy Attribute undefined !'); + if (!attribute_) + throw new ReferenceError('Connection Strategy Attribute undefined !'); const value_ = await this.prisma.cs_values.findFirst({ where: { id_cs_attribute: attribute_.id_cs_attribute, }, }); - if (!value_) throw new ReferenceError('Connection Strategy Value undefined !'); + if (!value_) + throw new ReferenceError('Connection Strategy Value undefined !'); authValues.push(this.crypto.decrypt(value_.value)); } return authValues; @@ -321,7 +332,7 @@ export class ConnectionsStrategiesService { } async getCredentials(projectId: string, type: string) { - try{ + try { const isCustomCred = await this.isCustomCredentials(projectId, type); const provider = extractProvider(type); const vertical = extractVertical(type); @@ -330,8 +341,10 @@ export class ConnectionsStrategiesService { throw new ReferenceError(`vertical not found for provider ${provider}`); const authStrategy = extractAuthMode(type); if (!authStrategy) - throw new ReferenceError(`auth strategy not found for provider ${provider}`); - + throw new ReferenceError( + `auth strategy not found for provider ${provider}`, + ); + if (isCustomCred) { //customer is using custom credentials (set in the webapp UI) //fetch the right credentials @@ -351,16 +364,16 @@ export class ConnectionsStrategiesService { SoftwareMode.cloud, ); } - }catch(error){ - throwTypedError(new ConnectionStrategiesError( - { - name: "GET_CREDENTIALS_ERROR", - message: "ConnectionsStrategiesService.getCredentials() call failed", - cause: error - } - ), this.logger) + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'GET_CREDENTIALS_ERROR', + message: 'ConnectionsStrategiesService.getCredentials() call failed', + cause: error, + }), + this.logger, + ); } - } async getConnectionStrategiesForProject(projectId: string) { @@ -371,13 +384,15 @@ export class ConnectionsStrategiesService { }, }); } catch (error) { - throwTypedError(new ConnectionStrategiesError( - { - name: "GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR", - message: "ConnectionsStrategiesService.getConnectionStrategiesForProject() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionStrategiesError({ + name: 'GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR', + message: + 'ConnectionsStrategiesService.getConnectionStrategiesForProject() call failed', + cause: error, + }), + this.logger, + ); } } @@ -410,7 +425,8 @@ export class ConnectionsStrategiesService { }, }); - if (!id_cs_entity) throw new ReferenceError('Connection strategy entity undefined !'); + if (!id_cs_entity) + throw new ReferenceError('Connection strategy entity undefined !'); for (let i = 0; i < attributes.length; i++) { const attribute_slug = attributes[i]; @@ -435,13 +451,15 @@ export class ConnectionsStrategiesService { } return cs; } catch (error) { - throwTypedError(new ConnectionStrategiesError( - { - name: "UPDATE_CONNECTION_STRATEGY_ERROR", - message: "ConnectionsStrategiesService.updateConnectionStrategy() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionStrategiesError({ + name: 'UPDATE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.updateConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); } } @@ -459,7 +477,8 @@ export class ConnectionsStrategiesService { id_connection_strategy: id_cs, }, }); - if (!id_cs_entity) throw new ReferenceError('Connection strategy entity undefined !'); + if (!id_cs_entity) + throw new ReferenceError('Connection strategy entity undefined !'); const attributes = await this.prisma.cs_attributes.findMany({ where: { @@ -500,13 +519,15 @@ export class ConnectionsStrategiesService { return deleteCS; } catch (error) { - throwTypedError(new ConnectionStrategiesError( - { - name: "DELETE_CONNECTION_STRATEGY_ERROR", - message: "ConnectionsStrategiesService.deleteConnectionStrategy() call failed", - cause: error - } - ), this.logger) + throwTypedError( + new ConnectionStrategiesError({ + name: 'DELETE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.deleteConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index e4c2cabd0..c54ed6848 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -13,7 +13,7 @@ import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { PrismaService } from '@@core/prisma/prisma.service'; import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; import { TicketingConnectionsService } from './ticketing/services/ticketing.connection.service'; -import { ConnectorCategory } from '@panora/shared'; +import { ConnectorCategory, CONNECTORS_METADATA } from '@panora/shared'; import { AccountingConnectionsService } from './accounting/services/accounting.connection.service'; import { MarketingAutomationConnectionsService } from './marketingautomation/services/marketingautomation.connection.service'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; @@ -114,13 +114,18 @@ export class ConnectionsController { ); break; } - // Performing Core Sync Service - this.coreSync.initialSync( - vertical.toLowerCase(), - providerName, - linkedUserId, - projectId, - ); + if ( + CONNECTORS_METADATA[vertical.toLowerCase()][providerName.toLowerCase()] + .active !== false + ) { + // Performing Core Sync Service for active connectors + this.coreSync.initialSync( + vertical.toLowerCase(), + providerName, + linkedUserId, + projectId, + ); + } res.redirect(returnUrl); } catch (error) { throwTypedError( diff --git a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts index 999ae0676..efd7d4449 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts @@ -1,7 +1,13 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -101,9 +107,7 @@ export class JiraConnectionService implements ITicketingConnectionService { }, ); const sites_scopes: JiraCloudIdInformation[] = res_.data; - this.logger.log( - 'sites scopes for jira are ----> ' + JSON.stringify(sites_scopes), - ); + const cloud_id: string = sites_scopes[0].id; //todo let db_res; const connection_token = uuidv4(); @@ -111,14 +115,6 @@ export class JiraConnectionService implements ITicketingConnectionService { const access_token = this.cryptoService.encrypt(data.access_token); const refresh_token = this.cryptoService.encrypt(data.refresh_token); - this.logger.log( - 'encrypted token is ----> ' + JSON.stringify(access_token), - ); - - this.logger.log( - 'encrypted refresh token is ----> ' + JSON.stringify(refresh_token), - ); - if (isNotUnique) { db_res = await this.prisma.connections.update({ where: { @@ -167,17 +163,18 @@ export class JiraConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_CALLBACK_TICKETING", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_TICKETING', message: `JiraConnectionService.handleCallback() call failed ---> ${format3rdPartyError( - "jira", + 'jira', Action.oauthCallback, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } @@ -190,16 +187,16 @@ export class JiraConnectionService implements ITicketingConnectionService { this.type, )) as OAuth2AuthData; - const formData = new URLSearchParams({ + const formData = { grant_type: 'refresh_token', client_id: CREDENTIALS.CLIENT_ID, client_secret: CREDENTIALS.CLIENT_SECRET, refresh_token: this.cryptoService.decrypt(refreshToken), - }); - + }; + const res = await axios.post( `https://auth.atlassian.com/oauth/token`, - formData.toString(), + formData, { headers: { 'Content-Type': 'application/json', @@ -221,17 +218,18 @@ export class JiraConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : jira '); } catch (error) { - throwTypedError(new ConnectionsError( - { - name: "HANDLE_OAUTH_REFRESH_TICKETING", + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_TICKETING', message: `JiraConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( - "jira", + 'jira', Action.oauthRefresh, - ActionType.POST + ActionType.POST, )}`, - cause: error - } - ), this.logger) + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/project-connectors/project-connectors.controller.ts b/packages/api/src/@core/project-connectors/project-connectors.controller.ts index 748c92610..75c250613 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.controller.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.controller.ts @@ -18,20 +18,6 @@ import { import { ProjectConnectorsService } from './project-connectors.service'; import { ProjectConnectorsDto } from './dto/project-connectors.dto'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; -export interface TypeCustom { - id_project: string; - crm_hubspot: boolean; - crm_zoho: boolean; - crm_zendesk: boolean; - crm_pipedrive: boolean; - crm_attio: boolean; - tcg_zendesk: boolean; - tcg_gorgias: boolean; - tcg_front: boolean; - tcg_jira: boolean; - tcg_gitlab: boolean; - crm_close: boolean; -} @ApiTags('project-connectors') @Controller('project-connectors') export class ProjectConnectorsController { @@ -63,10 +49,10 @@ export class ProjectConnectorsController { ); } - @Post('create') + /*@Post('create') async createConnectorsToProject(@Body() data: TypeCustom) { return await this.projectConnectorsService.createProjectConnectors(data); - } + }*/ @ApiOperation({ operationId: 'getConnectorsFromProject', diff --git a/packages/api/src/@core/project-connectors/project-connectors.service.ts b/packages/api/src/@core/project-connectors/project-connectors.service.ts index 35236c503..fcc44c191 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.service.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.service.ts @@ -1,9 +1,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; import { ConnectorSetError, throwTypedError } from '@@core/utils/errors'; -import { TypeCustom } from './project-connectors.controller'; @Injectable() export class ProjectConnectorsService { @@ -64,7 +62,7 @@ export class ProjectConnectorsService { } } - async createProjectConnectors(data: TypeCustom) { + /*async createProjectConnectors(data: TypeCustom) { try { const updateData: any = { id_connector_set: uuidv4(), @@ -96,7 +94,7 @@ export class ProjectConnectorsService { this.logger, ); } - } + }*/ async getConnectorsByProjectId(id_project: string) { try { diff --git a/packages/api/src/@core/sync/sync.service.ts b/packages/api/src/@core/sync/sync.service.ts index 56fe87899..6c5b893d5 100644 --- a/packages/api/src/@core/sync/sync.service.ts +++ b/packages/api/src/@core/sync/sync.service.ts @@ -152,7 +152,6 @@ export class CoreSyncService { break; case ConnectorCategory.Ticketing: - // logic tasks.push(() => this.TicketingUserSyncService.syncUsersForLinkedUser( provider, diff --git a/packages/api/src/@core/webhook/webhook.service.ts b/packages/api/src/@core/webhook/webhook.service.ts index 44a8b9197..7580f7a4b 100644 --- a/packages/api/src/@core/webhook/webhook.service.ts +++ b/packages/api/src/@core/webhook/webhook.service.ts @@ -135,18 +135,17 @@ export class WebhookService { active: true, }, }); - if (!webhooks) - throw ReferenceError(`Webhook not found for id_project=${projectId}`); + + // we dont deliver the webhook + if (!webhooks) return; const webhook = webhooks.find((wh) => { const scopes = wh.scope; return scopes.includes(eventType); }); - if (!webhook) - throw ReferenceError( - `Webhook not found for scope eventType=${eventType}`, - ); + // we dont deliver the webhook + if (!webhook) return; this.logger.log('handling webhook payload....'); @@ -202,20 +201,14 @@ export class WebhookService { active: true, }, }); - if (!webhooks) - throw ReferenceError( - `Webhook endpoint undefined for id_project=${projectId}`, - ); + if (!webhooks) return; const webhook = webhooks.find((wh) => { const scopes = wh.scope; return scopes.includes(eventType); }); - if (!webhook) - throw ReferenceError( - `Webhook not found for scope eventType=${eventType}`, - ); + if (!webhook) return; this.logger.log('handling webhook payload....'); diff --git a/packages/api/src/crm/company/sync/sync.service.ts b/packages/api/src/crm/company/sync/sync.service.ts index bf1eeb2b5..303d3cb6b 100644 --- a/packages/api/src/crm/company/sync/sync.service.ts +++ b/packages/api/src/crm/company/sync/sync.service.ts @@ -150,9 +150,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping companies syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/contact/sync/sync.service.ts b/packages/api/src/crm/contact/sync/sync.service.ts index 36464b3a7..5a5a7cb93 100644 --- a/packages/api/src/crm/contact/sync/sync.service.ts +++ b/packages/api/src/crm/contact/sync/sync.service.ts @@ -163,9 +163,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/deal/sync/sync.service.ts b/packages/api/src/crm/deal/sync/sync.service.ts index c8d25bf0b..5fa8d6419 100644 --- a/packages/api/src/crm/deal/sync/sync.service.ts +++ b/packages/api/src/crm/deal/sync/sync.service.ts @@ -152,9 +152,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping deals syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/engagement/sync/sync.service.ts b/packages/api/src/crm/engagement/sync/sync.service.ts index bade74680..82488abda 100644 --- a/packages/api/src/crm/engagement/sync/sync.service.ts +++ b/packages/api/src/crm/engagement/sync/sync.service.ts @@ -150,9 +150,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping engagements syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/note/sync/sync.service.ts b/packages/api/src/crm/note/sync/sync.service.ts index 5d6ea7296..f3fcddf32 100644 --- a/packages/api/src/crm/note/sync/sync.service.ts +++ b/packages/api/src/crm/note/sync/sync.service.ts @@ -145,9 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping notes syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/stage/sync/sync.service.ts b/packages/api/src/crm/stage/sync/sync.service.ts index c834c8152..a40b45f2e 100644 --- a/packages/api/src/crm/stage/sync/sync.service.ts +++ b/packages/api/src/crm/stage/sync/sync.service.ts @@ -160,9 +160,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping stages syncing... No ${integrationId} connection was found for linked stage ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/task/sync/sync.service.ts b/packages/api/src/crm/task/sync/sync.service.ts index 0b5d56064..a06300bd3 100644 --- a/packages/api/src/crm/task/sync/sync.service.ts +++ b/packages/api/src/crm/task/sync/sync.service.ts @@ -145,9 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tasks syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/crm/user/sync/sync.service.ts b/packages/api/src/crm/user/sync/sync.service.ts index 2207dd656..3d27c641a 100644 --- a/packages/api/src/crm/user/sync/sync.service.ts +++ b/packages/api/src/crm/user/sync/sync.service.ts @@ -145,9 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); + } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/account/sync/sync.service.ts b/packages/api/src/ticketing/account/sync/sync.service.ts index de10b5c5d..4f6b95ae8 100644 --- a/packages/api/src/ticketing/account/sync/sync.service.ts +++ b/packages/api/src/ticketing/account/sync/sync.service.ts @@ -143,9 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping accounts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name diff --git a/packages/api/src/ticketing/collection/sync/sync.service.ts b/packages/api/src/ticketing/collection/sync/sync.service.ts index 99b22243b..cb1c4dd95 100644 --- a/packages/api/src/ticketing/collection/sync/sync.service.ts +++ b/packages/api/src/ticketing/collection/sync/sync.service.ts @@ -141,9 +141,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping collections syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } const service: ICollectionService = diff --git a/packages/api/src/ticketing/comment/sync/sync.service.ts b/packages/api/src/ticketing/comment/sync/sync.service.ts index 8777e8a2c..10267c1a4 100644 --- a/packages/api/src/ticketing/comment/sync/sync.service.ts +++ b/packages/api/src/ticketing/comment/sync/sync.service.ts @@ -154,9 +154,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping comments syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/contact/sync/sync.service.ts b/packages/api/src/ticketing/contact/sync/sync.service.ts index f2f35176c..e45398b3c 100644 --- a/packages/api/src/ticketing/contact/sync/sync.service.ts +++ b/packages/api/src/ticketing/contact/sync/sync.service.ts @@ -156,9 +156,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/tag/sync/sync.service.ts b/packages/api/src/ticketing/tag/sync/sync.service.ts index 5dbb08c47..37e1726f4 100644 --- a/packages/api/src/ticketing/tag/sync/sync.service.ts +++ b/packages/api/src/ticketing/tag/sync/sync.service.ts @@ -154,9 +154,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tags syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/team/sync/sync.service.ts b/packages/api/src/ticketing/team/sync/sync.service.ts index 7a6413ec2..b76d1e977 100644 --- a/packages/api/src/ticketing/team/sync/sync.service.ts +++ b/packages/api/src/ticketing/team/sync/sync.service.ts @@ -143,9 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping teams syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/ticket/sync/sync.service.ts b/packages/api/src/ticketing/ticket/sync/sync.service.ts index 0b346f918..9b710b1c4 100644 --- a/packages/api/src/ticketing/ticket/sync/sync.service.ts +++ b/packages/api/src/ticketing/ticket/sync/sync.service.ts @@ -143,9 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tickets syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/src/ticketing/user/sync/sync.service.ts b/packages/api/src/ticketing/user/sync/sync.service.ts index 594cf4f43..8cfe82595 100644 --- a/packages/api/src/ticketing/user/sync/sync.service.ts +++ b/packages/api/src/ticketing/user/sync/sync.service.ts @@ -147,9 +147,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - throw ReferenceError( - `Connection undefined for id_linked_user=${linkedUserId} and integrationId=${integrationId}`, - ); } // get potential fieldMappings and extract the original properties name const customFieldMappings = diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index 03177ba33..2d08a9820 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -4500,20 +4500,6 @@ ] } }, - "/project-connectors/create": { - "post": { - "operationId": "ProjectConnectorsController_createConnectorsToProject", - "parameters": [], - "responses": { - "201": { - "description": "" - } - }, - "tags": [ - "project-connectors" - ] - } - }, "/ticketing/attachments": { "get": { "operationId": "getAttachments", From 44c567a8f53b123cd0a101ea29105913fb9fe9bf Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 14 Jun 2024 10:35:04 +0200 Subject: [PATCH 7/9] :bug: Fix await --- packages/api/src/@core/connections/connections.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index c54ed6848..25d4a693d 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -119,7 +119,7 @@ export class ConnectionsController { .active !== false ) { // Performing Core Sync Service for active connectors - this.coreSync.initialSync( + await this.coreSync.initialSync( vertical.toLowerCase(), providerName, linkedUserId, From 48396343784164c8dc7f22c156c097a050c51c98 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 14 Jun 2024 17:46:30 +0200 Subject: [PATCH 8/9] :twisted_rightwards_arrows: Fix merge --- packages/api/prisma/schema.prisma | 2 -- packages/api/src/@core/connections/connections.controller.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 507dea8ae..7e528d24a 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -656,8 +656,6 @@ model connector_sets { tcg_gorgias Boolean tcg_gitlab Boolean tcg_front Boolean - crm_zendesk Boolean - crm_close Boolean projects projects[] } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 25d4a693d..e468ce1e2 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -114,6 +114,7 @@ export class ConnectionsController { ); break; } + res.redirect(returnUrl); if ( CONNECTORS_METADATA[vertical.toLowerCase()][providerName.toLowerCase()] .active !== false @@ -126,7 +127,6 @@ export class ConnectionsController { projectId, ); } - res.redirect(returnUrl); } catch (error) { throwTypedError( new ConnectionsError({ From 2a65d3d494216f23f68b69ba0ecb913ad2f11fd1 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 14 Jun 2024 17:55:15 +0200 Subject: [PATCH 9/9] :bug: Fix build --- packages/shared/src/authUrl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index d55527f27..f2c25f2c8 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -118,6 +118,7 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { break; case 'jira': params = `audience=api.atlassian.com&${params}&prompt=consent&response_type=code`; + break; case 'jira_service_mgmt': params = `audience=api.atlassian.com&${params}&prompt=consen&response_type=codet`; break;