diff --git a/INTEGRATIONS.md b/INTEGRATIONS.md index c55d922d7..44ae184ad 100644 --- a/INTEGRATIONS.md +++ b/INTEGRATIONS.md @@ -18,7 +18,7 @@ First choose wisely which vertical the 3rd party belongs to among these: For the sake of the guide, now on we'll consider adding a 3rd party belonging to the `crm` vertical. -## 1. Add a new service for your 3rd Party +## 1. Add a new connection service for your 3rd Party Create a new folder with the name of your 3rd party. Let's call it *my3rdParty*. @@ -69,31 +69,40 @@ export class My3rdPartyConnectionService implements ICrmConnectionService { Now that you have the structure, check other 3rd parties implementations under `/@core/connections/crm/services` to build your functions. -## 2. Add a new connection to handle oAuth granting access +## 2. Enable your connection service to handle oAuth granting access -`cd @core/connections/crm/services/crm-connection.service.ts` +`cd @core/connections/crm/services/registry.service.ts` -Add your new 3rd party service to the `serviceMapping` object. +Add your new 3rd party service to the `registry` service. ```ts -private serviceMapping: { [key: string]: ICrmConnectionService } = { - "hubspot": this.hubspotConnectionService, - "zoho": this.zohoConnectionService, - "zendesk": this.zendeskConnectionService, - "freshsales": this.freshsalesConnectionService, - "pipedrive": this.pipedriveConnectionService, - // INSERT BELOW YOUR 3rd PARTY HERE - "my_3rd_party": this.my3rdPartyConnectionService -}; -``` +//ADD YOUR IMPORT +import { My3rdPartyConnectionService } from './my3rdParty/my3rdParty.service'; -Don't forget to add your service you've defined at step 1 inside the constructor. +@Injectable() +export class ServiceConnectionRegistry { + private serviceMap: Map; -```ts -constructor(private my3rdPartyConnectionService: My3rdPartyConnectionService) + constructor( + freshsales: FreshsalesConnectionService, + hubspot: HubspotConnectionService, + zoho: ZohoConnectionService, + zendesk: ZendeskConnectionService, + pipedrive: PipedriveConnectionService, + ) { + this.serviceMap = new Map(); + this.serviceMap.set('freshsales', freshsales); + this.serviceMap.set('hubspot', hubspot); + this.serviceMap.set('zoho', zoho); + this.serviceMap.set('zendesk', zendesk); + this.serviceMap.set('pipedrive', pipedrive); + //ADD YOUR SERVICE + this.serviceMap.set('my3rdParty', my3rdParty); + } +} ``` -Finally, don't forget to add your newly created service inside CrmConnectionModule under `/@core/connections/crm/crm-connection.module.ts` +Don't forget to ddd your service to the `CrmConnectionModule` module ! ```ts @Module({ @@ -101,20 +110,22 @@ Finally, don't forget to add your newly created service inside CrmConnectionModu providers: [ CrmConnectionsService, PrismaService, - FreshsalesConnectionService, - HubspotConnectionService, - PipedriveConnectionService, - ZendeskConnectionService, - ZohoConnectionService, + ServiceConnectionRegistry, LoggerService, WebhookService, EnvironmentService, EncryptionService, - //INSERT BELOW YOUR SERVICE + FreshsalesConnectionService, + HubspotConnectionService, + ZohoConnectionService, + ZendeskConnectionService, + PipedriveConnectionService, + //INSERT YOUR SERVICE HERE My3rdPartyConnectionService ], exports: [CrmConnectionsService], }) +export class CrmConnectionModule {} ``` # You want to map a common object to your new 3rd Party ? πŸ‘©β€πŸŽ€ @@ -303,6 +314,39 @@ export class ServiceRegistry { } ``` +Finally, add it under the `ContactModule` module ! + +```ts +@Module({ + imports: [ + BullModule.registerQueue({ + name: 'webhookDelivery', + }), + ], + controllers: [ContactController], + providers: [ + ContactService, + PrismaService, + FreshSalesService, + ZendeskService, + ZohoService, + PipedriveService, + HubspotService, + LoggerService, + FieldMappingService, + SyncContactsService, + WebhookService, + EncryptionService, + ServiceRegistry, + //INSERT YOUR SERVICE HERE + My3rdPartyService + ], + exports: [SyncContactsService], +}) +export class ContactModule {} + +``` + ### Congrats Hero ! πŸ¦Έβ€β™€οΈ ### You now have built a new integration with Panora diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 280167231..4a7dc8bd3 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Query, Res } from '@nestjs/common'; import { Response } from 'express'; -import { CrmConnectionsService } from './crm/services/crm-connection.service'; +import { CrmConnectionsService } from './crm/services/crm.connection.service'; import { LoggerService } from '@@core/logger/logger.service'; import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { PrismaService } from '@@core/prisma/prisma.service'; diff --git a/packages/api/src/@core/connections/connections.module.ts b/packages/api/src/@core/connections/connections.module.ts index 437919282..9983dd5be 100644 --- a/packages/api/src/@core/connections/connections.module.ts +++ b/packages/api/src/@core/connections/connections.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { CrmConnectionModule } from './crm/crm-connection.module'; +import { CrmConnectionModule } from './crm/crm.connection.module'; import { ConnectionsController } from './connections.controller'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; diff --git a/packages/api/src/@core/connections/crm/crm-connection.module.ts b/packages/api/src/@core/connections/crm/crm.connection.module.ts similarity index 88% rename from packages/api/src/@core/connections/crm/crm-connection.module.ts rename to packages/api/src/@core/connections/crm/crm.connection.module.ts index e02b8b192..d0a3c4c04 100644 --- a/packages/api/src/@core/connections/crm/crm-connection.module.ts +++ b/packages/api/src/@core/connections/crm/crm.connection.module.ts @@ -1,31 +1,33 @@ import { Module } from '@nestjs/common'; -import { CrmConnectionsService } from './services/crm-connection.service'; +import { CrmConnectionsService } from './services/crm.connection.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { FreshsalesConnectionService } from './services/freshsales/freshsales.service'; -import { HubspotConnectionService } from './services/hubspot/hubspot.service'; -import { PipedriveConnectionService } from './services/pipedrive/pipedrive.service'; -import { ZendeskConnectionService } from './services/zendesk/zendesk.service'; -import { ZohoConnectionService } from './services/zoho/zoho.service'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { WebhookModule } from '@@core/webhook/webhook.module'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; +import { ServiceConnectionRegistry } from './services/registry.service'; +import { FreshsalesConnectionService } from './services/freshsales/freshsales.service'; +import { HubspotConnectionService } from './services/hubspot/hubspot.service'; +import { ZohoConnectionService } from './services/zoho/zoho.service'; +import { ZendeskConnectionService } from './services/zendesk/zendesk.service'; +import { PipedriveConnectionService } from './services/pipedrive/pipedrive.service'; @Module({ imports: [WebhookModule], providers: [ CrmConnectionsService, PrismaService, - FreshsalesConnectionService, - HubspotConnectionService, - PipedriveConnectionService, - ZendeskConnectionService, - ZohoConnectionService, + ServiceConnectionRegistry, LoggerService, WebhookService, EnvironmentService, EncryptionService, + FreshsalesConnectionService, + HubspotConnectionService, + ZohoConnectionService, + ZendeskConnectionService, + PipedriveConnectionService, ], exports: [CrmConnectionsService], }) 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 similarity index 74% rename from packages/api/src/@core/connections/crm/services/crm-connection.service.ts rename to packages/api/src/@core/connections/crm/services/crm.connection.service.ts index 9e3df5c1c..4259b7769 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,25 +1,17 @@ import { Injectable } from '@nestjs/common'; -import { ZohoConnectionService } from './zoho/zoho.service'; import { NotFoundError, handleServiceError } from '@@core/utils/errors'; -import { HubspotConnectionService } from './hubspot/hubspot.service'; -import { PipedriveConnectionService } from './pipedrive/pipedrive.service'; -import { ZendeskConnectionService } from './zendesk/zendesk.service'; -import { FreshsalesConnectionService } from './freshsales/freshsales.service'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; -import { CallbackParams, ICrmConnectionService, RefreshParams } from '../types'; +import { CallbackParams, RefreshParams } from '../types'; +import { ServiceConnectionRegistry } from './registry.service'; @Injectable() export class CrmConnectionsService { constructor( - private zohoConnectionService: ZohoConnectionService, - private hubspotConnectionService: HubspotConnectionService, - private pipedriveConnectionService: PipedriveConnectionService, - private zendeskConnectionService: ZendeskConnectionService, - private freshsalesConnectionService: FreshsalesConnectionService, + private serviceRegistry: ServiceConnectionRegistry, private webhook: WebhookService, private logger: LoggerService, private prisma: PrismaService, @@ -27,14 +19,6 @@ export class CrmConnectionsService { this.logger.setContext(CrmConnectionsService.name); } - private serviceMapping: { [key: string]: ICrmConnectionService } = { - hubspot: this.hubspotConnectionService, - zoho: this.zohoConnectionService, - zendesk: this.zendeskConnectionService, - //'freshsales': this.freshsalesConnectionService, - pipedrive: this.pipedriveConnectionService, - }; - //STEP 1:[FRONTEND STEP] //create a frontend SDK snippet in which an authorization embedded link is set up so when users click // on it to grant access => they grant US the access and then when confirmed @@ -75,7 +59,8 @@ export class CrmConnectionsService { } const serviceName = providerName.toLowerCase(); - const service = this.serviceMapping[serviceName]; + const service = this.serviceRegistry.getService(serviceName); + if (!service) { throw new NotFoundError(`Unknown provider, found ${providerName}`); } @@ -114,7 +99,7 @@ export class CrmConnectionsService { ) { try { const serviceName = providerName.toLowerCase(); - const service = this.serviceMapping[serviceName]; + const service = this.serviceRegistry.getService(serviceName); if (!service) { throw new NotFoundError(`Unknown provider, found ${providerName}`); } 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 5c07b0aef..393bd5ad5 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 @@ -1,7 +1,12 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; -import { CallbackParams, HubspotOAuthResponse, ICrmConnectionService, RefreshParams } from '../../types'; +import { + CallbackParams, + HubspotOAuthResponse, + ICrmConnectionService, + RefreshParams, +} from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; import { Action, handleServiceError } from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; @@ -19,9 +24,7 @@ export class HubspotConnectionService implements ICrmConnectionService { this.logger.setContext(HubspotConnectionService.name); } - async handleCallback( - opts: CallbackParams - ) { + async handleCallback(opts: CallbackParams) { try { const { linkedUserId, projectId, code } = opts; this.logger.log( @@ -104,7 +107,7 @@ export class HubspotConnectionService implements ICrmConnectionService { async handleTokenRefresh(opts: RefreshParams) { try { - const {connectionId, refreshToken} = opts; + const { connectionId, refreshToken } = opts; const REDIRECT_URI = `${this.env.getOAuthRredirectBaseUrl()}/connections/oauth/callback`; //tocheck const formData = new URLSearchParams({ grant_type: 'refresh_token', 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 5adde8aa9..47dde21e9 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 @@ -1,7 +1,12 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; -import { CallbackParams, ICrmConnectionService, PipeDriveOAuthResponse, RefreshParams } from '../../types'; +import { + CallbackParams, + ICrmConnectionService, + PipeDriveOAuthResponse, + RefreshParams, +} from '../../types'; import { Action, handleServiceError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; @@ -19,9 +24,7 @@ export class PipedriveConnectionService implements ICrmConnectionService { this.logger.setContext(PipedriveConnectionService.name); } - async handleCallback( - opts: CallbackParams - ) { + async handleCallback(opts: CallbackParams) { try { const { linkedUserId, projectId, code } = opts; const isNotUnique = await this.prisma.connections.findFirst({ @@ -102,7 +105,7 @@ export class PipedriveConnectionService implements ICrmConnectionService { async handleTokenRefresh(opts: RefreshParams) { try { - const {connectionId, refreshToken} = opts; + const { connectionId, refreshToken } = opts; const REDIRECT_URI = `${this.env.getOAuthRredirectBaseUrl()}/connections/oauth/callback`; const formData = new URLSearchParams({ diff --git a/packages/api/src/@core/connections/crm/services/registry.service.ts b/packages/api/src/@core/connections/crm/services/registry.service.ts new file mode 100644 index 000000000..477411d5b --- /dev/null +++ b/packages/api/src/@core/connections/crm/services/registry.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@nestjs/common'; +import { ICrmConnectionService } from '../types'; +import { ZohoConnectionService } from './zoho/zoho.service'; +import { ZendeskConnectionService } from './zendesk/zendesk.service'; +import { PipedriveConnectionService } from './pipedrive/pipedrive.service'; +import { HubspotConnectionService } from './hubspot/hubspot.service'; +import { FreshsalesConnectionService } from './freshsales/freshsales.service'; + +@Injectable() +export class ServiceConnectionRegistry { + private serviceMap: Map; + + constructor( + freshsales: FreshsalesConnectionService, + hubspot: HubspotConnectionService, + zoho: ZohoConnectionService, + zendesk: ZendeskConnectionService, + pipedrive: PipedriveConnectionService, + ) { + this.serviceMap = new Map(); + this.serviceMap.set('freshsales', freshsales); + this.serviceMap.set('hubspot', hubspot); + this.serviceMap.set('zoho', zoho); + this.serviceMap.set('zendesk', zendesk); + this.serviceMap.set('pipedrive', pipedrive); + } + + getService(integrationId: string): ICrmConnectionService { + const service = this.serviceMap.get(integrationId); + if (!service) { + throw new Error( + `Connection Service not found for integration ID: ${integrationId}`, + ); + } + return service; + } +} 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 f4b44b95d..dffd0489d 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 @@ -1,7 +1,12 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { CallbackParams, ICrmConnectionService, RefreshParams, ZendeskOAuthResponse } from '../../types'; +import { + CallbackParams, + ICrmConnectionService, + RefreshParams, + ZendeskOAuthResponse, +} from '../../types'; import { Action, handleServiceError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; @@ -18,9 +23,7 @@ export class ZendeskConnectionService implements ICrmConnectionService { ) { this.logger.setContext(ZendeskConnectionService.name); } - async handleCallback( - opts: CallbackParams - ) { + async handleCallback(opts: CallbackParams) { try { const { linkedUserId, projectId, code } = opts; const isNotUnique = await this.prisma.connections.findFirst({ @@ -105,7 +108,7 @@ export class ZendeskConnectionService implements ICrmConnectionService { } async handleTokenRefresh(opts: RefreshParams) { try { - const {connectionId, refreshToken} = opts; + const { connectionId, refreshToken } = opts; const formData = new URLSearchParams({ grant_type: 'refresh_token', refresh_token: this.cryptoService.decrypt(refreshToken), 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 061ba3d83..045e1b1b5 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 @@ -1,7 +1,12 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { CallbackParams, ICrmConnectionService, RefreshParams, ZohoOAuthResponse } from '../../types'; +import { + CallbackParams, + ICrmConnectionService, + RefreshParams, + ZohoOAuthResponse, +} from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; import { Action, NotFoundError, handleServiceError } from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; @@ -25,9 +30,7 @@ export class ZohoConnectionService implements ICrmConnectionService { ) { this.logger.setContext(ZohoConnectionService.name); } - async handleCallback( - opts: CallbackParams - ) { + async handleCallback(opts: CallbackParams) { try { const { linkedUserId, projectId, code, location } = opts; if (!location) { @@ -108,7 +111,7 @@ export class ZohoConnectionService implements ICrmConnectionService { }, }); } - + return db_res; } catch (error) { handleServiceError(error, this.logger, 'zoho', Action.oauthCallback); @@ -116,7 +119,7 @@ export class ZohoConnectionService implements ICrmConnectionService { } async handleTokenRefresh(opts: RefreshParams) { try { - const {connectionId, refreshToken, account_url} = opts; + const { connectionId, refreshToken, account_url } = opts; const REDIRECT_URI = `${this.env.getOAuthRredirectBaseUrl()}/connections/oauth/callback`; const formData = new URLSearchParams({ grant_type: 'refresh_token', diff --git a/packages/api/src/@core/tasks/tasks.service.ts b/packages/api/src/@core/tasks/tasks.service.ts index 19a67f793..248247d6f 100644 --- a/packages/api/src/@core/tasks/tasks.service.ts +++ b/packages/api/src/@core/tasks/tasks.service.ts @@ -2,7 +2,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { PrismaService } from '../prisma/prisma.service'; -import { CrmConnectionsService } from '../connections/crm/services/crm-connection.service'; +import { CrmConnectionsService } from '../connections/crm/services/crm.connection.service'; import { LoggerService } from '@@core/logger/logger.service'; @Injectable()