From 743411e23e806e9d52bb9336d78491ed7188ca49 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Fri, 14 Jun 2024 05:56:36 -0400 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20Add=20Affinity=20CRM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/types/original/original.crm.ts | 20 ++ .../api/src/crm/company/company.module.ts | 2 + .../crm/company/services/affinity/index.ts | 108 +++++++++++ .../crm/company/services/affinity/mappers.ts | 86 +++++++++ .../crm/company/services/affinity/types.ts | 55 ++++++ .../src/crm/company/types/mappingsTypes.ts | 6 + .../api/src/crm/contact/contact.module.ts | 2 + .../crm/contact/services/affinity/index.ts | 115 +++++++++++ .../crm/contact/services/affinity/mappers.ts | 108 +++++++++++ .../crm/contact/services/affinity/types.ts | 57 ++++++ .../src/crm/contact/types/mappingsTypes.ts | 6 + packages/api/src/crm/deal/deal.module.ts | 2 + .../src/crm/deal/services/affinity/index.ts | 128 +++++++++++++ .../src/crm/deal/services/affinity/mappers.ts | 129 +++++++++++++ .../src/crm/deal/services/affinity/types.ts | 26 +++ .../api/src/crm/deal/types/mappingsTypes.ts | 6 + packages/api/src/crm/note/note.module.ts | 2 + .../src/crm/note/services/affinity/index.ts | 105 ++++++++++ .../src/crm/note/services/affinity/mappers.ts | 181 ++++++++++++++++++ .../src/crm/note/services/affinity/types.ts | 33 ++++ .../api/src/crm/note/types/mappingsTypes.ts | 6 + .../src/crm/user/services/affinity/index.ts | 68 +++++++ .../src/crm/user/services/affinity/mappers.ts | 55 ++++++ .../src/crm/user/services/affinity/types.ts | 27 +++ .../api/src/crm/user/types/mappingsTypes.ts | 6 + packages/api/src/crm/user/user.module.ts | 2 + packages/shared/src/connectors/enum.ts | 3 +- packages/shared/src/connectors/index.ts | 2 +- 28 files changed, 1344 insertions(+), 2 deletions(-) create mode 100644 packages/api/src/crm/company/services/affinity/index.ts create mode 100644 packages/api/src/crm/company/services/affinity/mappers.ts create mode 100644 packages/api/src/crm/company/services/affinity/types.ts create mode 100644 packages/api/src/crm/contact/services/affinity/index.ts create mode 100644 packages/api/src/crm/contact/services/affinity/mappers.ts create mode 100644 packages/api/src/crm/contact/services/affinity/types.ts create mode 100644 packages/api/src/crm/deal/services/affinity/index.ts create mode 100644 packages/api/src/crm/deal/services/affinity/mappers.ts create mode 100644 packages/api/src/crm/deal/services/affinity/types.ts create mode 100644 packages/api/src/crm/note/services/affinity/index.ts create mode 100644 packages/api/src/crm/note/services/affinity/mappers.ts create mode 100644 packages/api/src/crm/note/services/affinity/types.ts create mode 100644 packages/api/src/crm/user/services/affinity/index.ts create mode 100644 packages/api/src/crm/user/services/affinity/mappers.ts create mode 100644 packages/api/src/crm/user/services/affinity/types.ts 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 3f06b1c10..cb10cab2d 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -1,3 +1,13 @@ +import { AffinityUserInput, AffinityUserOutput } from '@crm/user/services/affinity/types'; + +import { AffinityNoteInput, AffinityNoteOutput } from '@crm/note/services/affinity/types'; + +import { AffinityDealInput, AffinityDealOutput } from '@crm/deal/services/affinity/types'; + +import { AffinityCompanyInput, AffinityCompanyOutput } from '@crm/company/services/affinity/types'; + +import { AffinityContactInput, AffinityContactOutput } from '@crm/contact/services/affinity/types'; + import { AttioCompanyOutput } from '@crm/company/services/attio/types'; import { HubspotCompanyOutput } from '@crm/company/services/hubspot/types'; import { PipedriveCompanyOutput } from '@crm/company/services/pipedrive/types'; @@ -129,6 +139,7 @@ import { /* contact */ export type OriginalContactInput = + | AffinityContactInput | HubspotContactInput | ZohoContactInput | ZendeskContactInput @@ -138,6 +149,7 @@ export type OriginalContactInput = /* deal */ export type OriginalDealInput = + | AffinityDealInput | HubspotDealOutput | ZohoDealOutput | ZendeskDealOutput @@ -146,6 +158,7 @@ export type OriginalDealInput = /* company */ export type OriginalCompanyInput = + | AffinityCompanyInput | HubspotCompanyOutput | ZohoCompanyOutput | ZendeskCompanyOutput @@ -163,6 +176,7 @@ export type OriginalEngagementInput = /* note */ export type OriginalNoteInput = + | AffinityNoteInput | HubspotNoteInput | ZohoNoteInput | ZendeskNoteInput @@ -189,6 +203,7 @@ export type OriginalStageInput = /* user */ export type OriginalUserInput = + | AffinityUserInput | HubspotUserInput | ZohoUserInput | ZendeskUserInput @@ -208,6 +223,7 @@ export type CrmObjectInput = /* OUTPUT */ export type OriginalContactOutput = + | AffinityContactOutput | HubspotContactOutput | ZohoContactOutput | ZendeskContactOutput @@ -217,6 +233,7 @@ export type OriginalContactOutput = /* deal */ export type OriginalDealOutput = + | AffinityDealOutput | HubspotDealOutput | ZohoDealOutput | ZendeskDealOutput @@ -225,6 +242,7 @@ export type OriginalDealOutput = /* company */ export type OriginalCompanyOutput = + | AffinityCompanyOutput | HubspotCompanyOutput | ZohoCompanyOutput | ZendeskCompanyOutput @@ -242,6 +260,7 @@ export type OriginalEngagementOutput = /* note */ export type OriginalNoteOutput = + | AffinityNoteOutput | HubspotNoteOutput | ZohoNoteOutput | ZendeskNoteOutput @@ -268,6 +287,7 @@ export type OriginalStageOutput = /* user */ export type OriginalUserOutput = + | AffinityUserOutput | HubspotUserOutput | ZohoUserOutput | ZendeskUserOutput diff --git a/packages/api/src/crm/company/company.module.ts b/packages/api/src/crm/company/company.module.ts index 141521f89..69ced40de 100644 --- a/packages/api/src/crm/company/company.module.ts +++ b/packages/api/src/crm/company/company.module.ts @@ -1,3 +1,4 @@ +import { AffinityService } from './services/affinity'; import { Module } from '@nestjs/common'; import { CompanyController } from './company.controller'; import { SyncService } from './sync/sync.service'; @@ -40,6 +41,7 @@ import { CloseService } from './services/close'; HubspotService, AttioService, CloseService, + AffinityService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/company/services/affinity/index.ts b/packages/api/src/crm/company/services/affinity/index.ts new file mode 100644 index 000000000..70e4ee881 --- /dev/null +++ b/packages/api/src/crm/company/services/affinity/index.ts @@ -0,0 +1,108 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Injectable } from '@nestjs/common'; +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 { EncryptionService } from '@@core/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ICompanyService } from '@crm/company/types'; +import { ServiceRegistry } from '../registry.service'; +import { AffinityCompanyInput, AffinityCompanyOutput } from './types'; + +@Injectable() +export class AffinityService implements ICompanyService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.company.toUpperCase() + ':' + AffinityService.name, + ); + this.registry.registerService('affinity', this); + } + + async addCompany( + companyData: AffinityCompanyInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + + const resp = await axios.post( + `${connection.account_url}/organizations`, + JSON.stringify({ + data: companyData, + }), + { + headers: { + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + 'Content-Type': 'application/json', + }, + }, + ); + return { + data: resp.data, + message: 'Affinity company created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.company, + ActionType.POST, + ); + } + } + + async syncCompanies( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + const resp = await axios.get( + `${connection.account_url}/organizations`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data, + message: 'Affinity companies retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.company, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/company/services/affinity/mappers.ts b/packages/api/src/crm/company/services/affinity/mappers.ts new file mode 100644 index 000000000..d1126c253 --- /dev/null +++ b/packages/api/src/crm/company/services/affinity/mappers.ts @@ -0,0 +1,86 @@ +import { AffinityCompanyInput, AffinityCompanyOutput } 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 AffinityCompanyMapper implements ICompanyMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + async desunify( + source: UnifiedCompanyInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: AffinityCompanyInput = { + name: source.name + }; + + // Affinity company does not have attribute for email address + // Affinity Company doest not have direct mapping of number of employees + + 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: AffinityCompanyOutput | AffinityCompanyOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleCompanyToUnified(source, customFieldMappings); + } + // Handling array of AffinityCompanyOutput + return Promise.all( + source.map((company) => + this.mapSingleCompanyToUnified(company, customFieldMappings), + ), + ); + } + + private async mapSingleCompanyToUnified( + company: AffinityCompanyOutput, + 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 = {}; + + + + return { + remote_id: company.id, + name: company.name, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/company/services/affinity/types.ts b/packages/api/src/crm/company/services/affinity/types.ts new file mode 100644 index 000000000..eeb82063e --- /dev/null +++ b/packages/api/src/crm/company/services/affinity/types.ts @@ -0,0 +1,55 @@ +interface AffinityCompany { + id: number + name: string + domain: string + domains: string[] + global: boolean + person_ids: number[] + opportunity_ids: number[] + list_entries: ListEntry[] + interaction_dates: InteractionDates + interactions: Interactions +} + +interface ListEntry { + id: number + list_id: number + creator_id: number + entity_id: number + created_at: string +} + +interface InteractionDates { + first_email_date: string + last_email_date: string + last_event_date: string + last_chat_message_date: string + last_interaction_date: string + next_event_date: string + first_event_date: string +} + +interface Interactions { + first_email: InteractionsType + last_email: InteractionsType + last_event: InteractionsType + last_chat_message: InteractionsType + last_interaction: InteractionsType + next_event: InteractionsType + first_event: InteractionsType +} + +interface InteractionsType { + date: string + person_ids: number[] +} + +export type AffinityCompanyInput = { + name: string, + domain?: string, + person_ids?: number[] +} + +export type AffinityCompanyOutput = Partial + + diff --git a/packages/api/src/crm/company/types/mappingsTypes.ts b/packages/api/src/crm/company/types/mappingsTypes.ts index d394f8bb8..8ebf76d2a 100644 --- a/packages/api/src/crm/company/types/mappingsTypes.ts +++ b/packages/api/src/crm/company/types/mappingsTypes.ts @@ -1,3 +1,4 @@ +import { AffinityCompanyMapper } from '../services/affinity/mappers'; import { AttioCompanyMapper } from '../services/attio/mappers'; import { HubspotCompanyMapper } from '../services/hubspot/mappers'; import { PipedriveCompanyMapper } from '../services/pipedrive/mappers'; @@ -12,6 +13,7 @@ const pipedriveCompanyMapper = new PipedriveCompanyMapper(); const attioCompanyMapper = new AttioCompanyMapper(); const closeCompanyMapper = new CloseCompanyMapper(); +const affinityCompanyMapper = new AffinityCompanyMapper(); export const companyUnificationMapping = { hubspot: { unify: hubspotCompanyMapper.unify.bind(hubspotCompanyMapper), @@ -37,4 +39,8 @@ export const companyUnificationMapping = { unify: closeCompanyMapper.unify.bind(closeCompanyMapper), desunify: closeCompanyMapper.desunify.bind(closeCompanyMapper), }, + affinity: { + unify: affinityCompanyMapper.unify.bind(affinityCompanyMapper), + desunify: affinityCompanyMapper.desunify.bind(affinityCompanyMapper), + }, }; diff --git a/packages/api/src/crm/contact/contact.module.ts b/packages/api/src/crm/contact/contact.module.ts index d592fe7d0..f377322e6 100644 --- a/packages/api/src/crm/contact/contact.module.ts +++ b/packages/api/src/crm/contact/contact.module.ts @@ -1,3 +1,4 @@ +import { AffinityService } from './services/affinity'; import { Module } from '@nestjs/common'; import { ContactService } from './services/contact.service'; import { ContactController } from './contact.controller'; @@ -42,6 +43,7 @@ import { CloseService } from './services/close'; PipedriveService, HubspotService, CloseService, + AffinityService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/contact/services/affinity/index.ts b/packages/api/src/crm/contact/services/affinity/index.ts new file mode 100644 index 000000000..7ed4ab6bc --- /dev/null +++ b/packages/api/src/crm/contact/services/affinity/index.ts @@ -0,0 +1,115 @@ +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 { AffinityContactInput, AffinityContactOutput } from './types'; + +@Injectable() +export class AffinityService implements IContactService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.contact.toUpperCase() + ':' + AffinityService.name, + ); + this.registry.registerService('affinity', this); + } + + async addContact( + contactData: AffinityContactInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + + const resp = await axios.post( + `${connection.account_url}/persons`, + JSON.stringify({ + data: contactData, + }), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data, + message: 'affinity contact created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.contact, + ActionType.POST, + ); + } + return; + } + + async syncContacts( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + // console.log('Before Axios'); + // console.log(this.cryptoService.decrypt(connection.access_token)); + + const resp = await axios.get( + `${connection.account_url}/persons`, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + console.log('After Axios'); + + return { + data: resp.data, + message: 'Affinity contacts retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.contact, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/contact/services/affinity/mappers.ts b/packages/api/src/crm/contact/services/affinity/mappers.ts new file mode 100644 index 000000000..478e6ca74 --- /dev/null +++ b/packages/api/src/crm/contact/services/affinity/mappers.ts @@ -0,0 +1,108 @@ +import { Address } from '@crm/@lib/@types'; +import { + UnifiedContactInput, + UnifiedContactOutput, +} from '@crm/contact/types/model.unified'; +import { IContactMapper } from '@crm/contact/types'; +import { AffinityContactInput, AffinityContactOutput } from './types'; +import { Utils } from '@crm/@lib/@utils'; + +export class AffinityContactMapper 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' and 'phone_numbers' arrays contain at least one entry + + + const result: AffinityContactInput = { + first_name: source.first_name, + last_name: source.last_name, + emails: source.email_addresses?.map((e) => e.email_address), + + }; + + + + 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: AffinityContactOutput | AffinityContactOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleContactToUnified(source, customFieldMappings); + } + + // Handling array of HubspotContactOutput + return Promise.all( + source.map((contact) => + this.mapSingleContactToUnified(contact, customFieldMappings), + ), + ); + } + + private async mapSingleContactToUnified( + contact: AffinityContactOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = contact[mapping.remote_id]; + } + } + // const address: Address = { + // street_1: '', + // city: '', + // state: '', + // postal_code: '', + // country: '', + // }; + const opts: any = {}; + + return { + remote_id: contact.id, + first_name: contact.first_name, + last_name: contact.last_name, + // user_id: contact.values.created_by[0]?.referenced_actor_id, + email_addresses: contact.emails?.map((e) => ({ + email_address: e, + email_address_type: '', + })), // Map each email + // phone_numbers: contact.values.phone_numbers?.map((p) => ({ + // phone_number: p.original_phone_number, + // phone_type: p.attribute_type ? p.attribute_type : '', + // })), // Map each phone number, + field_mappings, + // addresses: [address], + ...opts, + }; + } +} diff --git a/packages/api/src/crm/contact/services/affinity/types.ts b/packages/api/src/crm/contact/services/affinity/types.ts new file mode 100644 index 000000000..35171fbce --- /dev/null +++ b/packages/api/src/crm/contact/services/affinity/types.ts @@ -0,0 +1,57 @@ +interface AffinityContact { + id: number + type: number + first_name: string + last_name: string + primary_email: string + emails: string[] + organization_ids: number[] + opportunity_ids: number[] + current_organization_ids: number[] + list_entries: ListEntry[] + interaction_dates: InteractionDates + interactions: Interactions +} + +export type AffinityContactInput = { + first_name: string, + last_name: string, + emails: string[], + organization_ids?: number[] +} + +interface ListEntry { + id: number + list_id: number + creator_id: number + entity_id: number + created_at: string +} + +interface InteractionDates { + first_email_date: string + last_email_date: string + last_event_date: string + last_chat_message_date: string + last_interaction_date: string + next_event_date: string + first_event_date: string +} + +interface Interactions { + first_email: InteractionsType + last_email: InteractionsType + last_event: InteractionsType + last_chat_message: InteractionsType + last_interaction: InteractionsType + next_event: InteractionsType + first_event: InteractionsType +} + +interface InteractionsType { + date: string + person_ids: number[] +} + +export type AffinityContactOutput = Partial; + diff --git a/packages/api/src/crm/contact/types/mappingsTypes.ts b/packages/api/src/crm/contact/types/mappingsTypes.ts index d1cce1878..b78796402 100644 --- a/packages/api/src/crm/contact/types/mappingsTypes.ts +++ b/packages/api/src/crm/contact/types/mappingsTypes.ts @@ -1,3 +1,4 @@ +import { AffinityContactMapper } from '../services/affinity/mappers'; import { AttioContactMapper } from '../services/attio/mappers'; import { HubspotContactMapper } from '../services/hubspot/mappers'; import { PipedriveContactMapper } from '../services/pipedrive/mappers'; @@ -12,6 +13,7 @@ const pipedriveContactMapper = new PipedriveContactMapper(); const attioContactMapper = new AttioContactMapper(); const closeContactMapper = new CloseContactMapper(); +const affinityContactMapper = new AffinityContactMapper(); export const contactUnificationMapping = { hubspot: { unify: hubspotContactMapper.unify.bind(hubspotContactMapper), @@ -37,4 +39,8 @@ export const contactUnificationMapping = { unify: closeContactMapper.unify.bind(closeContactMapper), desunify: closeContactMapper.desunify.bind(closeContactMapper), }, + affinity: { + unify: affinityContactMapper.unify.bind(affinityContactMapper), + desunify: affinityContactMapper.desunify.bind(affinityContactMapper), + }, }; diff --git a/packages/api/src/crm/deal/deal.module.ts b/packages/api/src/crm/deal/deal.module.ts index e13d5cdf8..c3ca5e622 100644 --- a/packages/api/src/crm/deal/deal.module.ts +++ b/packages/api/src/crm/deal/deal.module.ts @@ -1,3 +1,4 @@ +import { AffinityService } from './services/affinity'; import { Module } from '@nestjs/common'; import { DealController } from './deal.controller'; import { SyncService } from './sync/sync.service'; @@ -40,6 +41,7 @@ import { CloseService } from './services/close'; PipedriveService, HubspotService, CloseService, + AffinityService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/deal/services/affinity/index.ts b/packages/api/src/crm/deal/services/affinity/index.ts new file mode 100644 index 000000000..2c5f4934d --- /dev/null +++ b/packages/api/src/crm/deal/services/affinity/index.ts @@ -0,0 +1,128 @@ +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 { AffinityDealInput, AffinityDealOutput } from './types'; +@Injectable() +export class AffinityService implements IDealService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.deal.toUpperCase() + ':' + AffinityService.name, + ); + this.registry.registerService('affinity', this); + } + async addDeal( + dealData: AffinityDealInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + + const list_resp = await axios.get( + `${connection.account_url}/lists`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + for (const list of list_resp.data) { + if (list.type === 8) { + dealData.list_id = list.id; + } + + } + + const resp = await axios.post( + `${connection.account_url}/opportunities`, + JSON.stringify(dealData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + return { + data: resp?.data, + message: 'Affinity deal created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + 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: 'affinity', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/opportunity/`; + const resp = await axios.get( + `${connection.account_url}/opportunities`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced affinity deals !`); + + return { + data: resp?.data?.data, + message: 'Affinity deals retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.deal, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/deal/services/affinity/mappers.ts b/packages/api/src/crm/deal/services/affinity/mappers.ts new file mode 100644 index 000000000..6998a84d8 --- /dev/null +++ b/packages/api/src/crm/deal/services/affinity/mappers.ts @@ -0,0 +1,129 @@ +import { AffinityDealInput, AffinityDealOutput } 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 AffinityDealMapper implements IDealMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedDealInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + + let opts: any = {}; + + if (source.company_id) { + const organization_id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + if (organization_id) { + opts = { + ...opts, + organization_ids: [organization_id] + } + } + } + + const result: AffinityDealInput = { + name: source.name, + list_id: 21, + ...opts + + }; + + 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: AffinityDealOutput | AffinityDealOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleDealToUnified(source, customFieldMappings); + } + // Handling array of AffinityDealOutput + return Promise.all( + source.map((deal) => + this.mapSingleDealToUnified(deal, customFieldMappings), + ), + ); + } + + private async mapSingleDealToUnified( + deal: AffinityDealOutput, + 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[mapping.remote_id]; + } + } + + let opts: any = {} + + if (deal.list_entries) { + const user_id = await this.utils.getUserUuidFromRemoteId( + String(deal.list_entries[0].creator_id), + 'affinity' + ); + if (user_id) { + opts = { + ...opts, + user_id + } + } + } + + if (deal.organization_ids && deal.organization_ids.length > 0) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + String(deal.organization_ids[0]), + 'affinity' + ); + + if (company_id) { + opts = { + ...opts, + company_id + } + } + } + + + + + return { + remote_id: String(deal.id), + name: deal.name, + description: '', // Placeholder if there's no direct mapping + amount: 0, + field_mappings, + ...opts + }; + } +} diff --git a/packages/api/src/crm/deal/services/affinity/types.ts b/packages/api/src/crm/deal/services/affinity/types.ts new file mode 100644 index 000000000..ba0bd1bd6 --- /dev/null +++ b/packages/api/src/crm/deal/services/affinity/types.ts @@ -0,0 +1,26 @@ +interface AffinityDeal { + id: number + name: string + person_ids: number[] + organization_ids: number[] + list_entries: ListEntry[] +} + +interface ListEntry { + id: number + creator_id: number + list_id: number + entity_id: number + entity_type: number + created_at: string +} + +export type AffinityDealInput = { + name: string, + list_id: number, + person_ids?: number[], + organization_ids?: number[] +} + +export type AffinityDealOutput = Partial + diff --git a/packages/api/src/crm/deal/types/mappingsTypes.ts b/packages/api/src/crm/deal/types/mappingsTypes.ts index 729c27586..ded67570e 100644 --- a/packages/api/src/crm/deal/types/mappingsTypes.ts +++ b/packages/api/src/crm/deal/types/mappingsTypes.ts @@ -1,3 +1,4 @@ +import { AffinityDealMapper } from '../services/affinity/mappers'; import { HubspotDealMapper } from '../services/hubspot/mappers'; import { PipedriveDealMapper } from '../services/pipedrive/mappers'; import { ZendeskDealMapper } from '../services/zendesk/mappers'; @@ -10,6 +11,7 @@ const zohoDealMapper = new ZohoDealMapper(); const pipedriveDealMapper = new PipedriveDealMapper(); const closeDealMapper = new CloseDealMapper(); +const affinityDealMapper = new AffinityDealMapper(); export const dealUnificationMapping = { hubspot: { unify: hubspotDealMapper.unify.bind(hubspotDealMapper), @@ -31,4 +33,8 @@ export const dealUnificationMapping = { unify: closeDealMapper.unify.bind(closeDealMapper), desunify: closeDealMapper.desunify.bind(closeDealMapper), }, + affinity: { + unify: affinityDealMapper.unify.bind(affinityDealMapper), + desunify: affinityDealMapper.desunify.bind(affinityDealMapper), + }, }; diff --git a/packages/api/src/crm/note/note.module.ts b/packages/api/src/crm/note/note.module.ts index ba97f6b97..27e0d7ced 100644 --- a/packages/api/src/crm/note/note.module.ts +++ b/packages/api/src/crm/note/note.module.ts @@ -1,3 +1,4 @@ +import { AffinityService } from './services/affinity'; import { Module } from '@nestjs/common'; import { NoteController } from './note.controller'; import { SyncService } from './sync/sync.service'; @@ -40,6 +41,7 @@ import { CloseService } from './services/close'; PipedriveService, HubspotService, CloseService, + AffinityService, ], exports: [ SyncService, diff --git a/packages/api/src/crm/note/services/affinity/index.ts b/packages/api/src/crm/note/services/affinity/index.ts new file mode 100644 index 000000000..f396996b2 --- /dev/null +++ b/packages/api/src/crm/note/services/affinity/index.ts @@ -0,0 +1,105 @@ +import { Injectable } from '@nestjs/common'; +import { INoteService } from '@crm/note/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { AffinityNoteInput, AffinityNoteOutput } 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 AffinityService implements INoteService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.note.toUpperCase() + ':' + AffinityService.name, + ); + this.registry.registerService('affinity', this); + } + async addNote( + noteData: AffinityNoteInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'affinity', + vertical: 'crm', + }, + }); + const resp = await axios.post( + `${connection.account_url}/notes`, + JSON.stringify(noteData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data, + message: 'Affinity note created', + statusCode: 201, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + 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: 'affinity', + vertical: 'crm', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/notes`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced affinity notes !`); + return { + data: resp?.data, + message: 'Affinity notes retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.note, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/note/services/affinity/mappers.ts b/packages/api/src/crm/note/services/affinity/mappers.ts new file mode 100644 index 000000000..34d1d724c --- /dev/null +++ b/packages/api/src/crm/note/services/affinity/mappers.ts @@ -0,0 +1,181 @@ +import { AffinityNoteInput, AffinityNoteOutput } 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 AffinityNoteMapper implements INoteMapper { + private readonly utils: Utils; + + constructor() { + this.utils = new Utils(); + } + + async desunify( + source: UnifiedNoteInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + + + let opts: any = {}; + + if (source.user_id) { + const creator_id = await this.utils.getRemoteIdFromUserUuid(source.user_id); + if (creator_id) { + opts = { + ...opts, + creator_id + } + } + } + + if (source.company_id) { + const organization_id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + if (organization_id) { + opts = { + ...opts, + organization_ids: [organization_id] + } + } + } + + if (source.contact_id) { + const person_id = await this.utils.getRemoteIdFromContactUuid(source.contact_id); + if (person_id) { + opts = { + ...opts, + person_ids: [person_id] + } + } + } + + if (source.deal_id) { + const opportunity_id = await this.utils.getRemoteIdFromDealUuid(source.deal_id); + if (opportunity_id) { + opts = { + ...opts, + opportunity_ids: [opportunity_id] + } + } + } + + const result: AffinityNoteInput = { + content: source.content, + ...opts + }; + + 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: AffinityNoteOutput | AffinityNoteOutput[], + 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: AffinityNoteOutput, + 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.creator_id) { + const user_id = await this.utils.getUserUuidFromRemoteId( + String(note.creator_id), + 'affinity' + ); + if (user_id) { + opts = { + ...opts, + user_id + } + } + } + + if (note.organization_ids && note.organization_ids.length > 0) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + String(note.organization_ids[0]), + 'affinity' + ); + if (company_id) { + opts = { + ...opts, + company_id + } + } + } + + if (note.person_ids && note.person_ids.length > 0) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + String(note.person_ids[0]), + 'affinity' + ); + if (contact_id) { + opts = { + ...opts, + contact_id + } + } + } + + if (note.opportunity_ids && note.opportunity_ids.length > 0) { + const deal_id = await this.utils.getDealUuidFromRemoteId( + String(note.opportunity_ids[0]), + 'affinity' + ); + if (deal_id) { + opts = { + ...opts, + deal_id + } + } + } + + + + return { + remote_id: note.id, + content: note.content, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/note/services/affinity/types.ts b/packages/api/src/crm/note/services/affinity/types.ts new file mode 100644 index 000000000..1b5fdfb32 --- /dev/null +++ b/packages/api/src/crm/note/services/affinity/types.ts @@ -0,0 +1,33 @@ +interface AffinityNote { + id: number + creator_id: number + person_ids: number[] + associated_person_ids: number[] + interaction_person_ids: number[] + interaction_id: number + interaction_type: number + is_meeting: boolean + mentioned_person_ids: number[] + organization_ids: number[] + opportunity_ids: number[] + parent_id: any + content: string + type: number + created_at: string + updated_at: string +} + +export type AffinityNoteInput = { + content: string, + person_ids?: number[], + organization_ids?: number[], + opportunity_ids?: number[], + type?: number, + parent_id?: number, + creator_id?: number, + created_at?: string +} + +export type AffinityNoteOutput = Partial + + diff --git a/packages/api/src/crm/note/types/mappingsTypes.ts b/packages/api/src/crm/note/types/mappingsTypes.ts index 43b7f3446..f9d4337be 100644 --- a/packages/api/src/crm/note/types/mappingsTypes.ts +++ b/packages/api/src/crm/note/types/mappingsTypes.ts @@ -1,3 +1,4 @@ +import { AffinityNoteMapper } from '../services/affinity/mappers'; import { HubspotNoteMapper } from '../services/hubspot/mappers'; import { PipedriveNoteMapper } from '../services/pipedrive/mappers'; import { ZendeskNoteMapper } from '../services/zendesk/mappers'; @@ -10,6 +11,7 @@ const zohoNoteMapper = new ZohoNoteMapper(); const pipedriveNoteMapper = new PipedriveNoteMapper(); const closeNoteMapper = new CloseNoteMapper(); +const affinityNoteMapper = new AffinityNoteMapper(); export const noteUnificationMapping = { hubspot: { unify: hubspotNoteMapper.unify.bind(hubspotNoteMapper), @@ -31,4 +33,8 @@ export const noteUnificationMapping = { unify: closeNoteMapper.unify.bind(closeNoteMapper), desunify: closeNoteMapper.desunify.bind(closeNoteMapper), }, + affinity: { + unify: affinityNoteMapper.unify.bind(affinityNoteMapper), + desunify: affinityNoteMapper.desunify.bind(affinityNoteMapper), + }, }; diff --git a/packages/api/src/crm/user/services/affinity/index.ts b/packages/api/src/crm/user/services/affinity/index.ts new file mode 100644 index 000000000..745b4c433 --- /dev/null +++ b/packages/api/src/crm/user/services/affinity/index.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { IUserService } from '@crm/user/types'; +import { CrmObject } from '@crm/@lib/@types'; +import { AffinityUserOutput } 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 AffinityService implements IUserService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.user.toUpperCase() + ':' + AffinityService.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 resp = await axios.get( + `${connection.account_url}/auth/whoami`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + this.logger.log(`Synced close users !`); + + return { + data: resp?.data, + message: 'Affinity users retrieved', + statusCode: 200, + }; + } catch (error) { + handleServiceError( + error, + this.logger, + 'Affinity', + CrmObject.user, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/crm/user/services/affinity/mappers.ts b/packages/api/src/crm/user/services/affinity/mappers.ts new file mode 100644 index 000000000..4e7afe0e0 --- /dev/null +++ b/packages/api/src/crm/user/services/affinity/mappers.ts @@ -0,0 +1,55 @@ +import { AffinityUserInput, AffinityUserOutput } from './types'; +import { + UnifiedUserInput, + UnifiedUserOutput, +} from '@crm/user/types/model.unified'; +import { IUserMapper } from '@crm/user/types'; + +export class AffinityUserMapper implements IUserMapper { + desunify( + source: UnifiedUserInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): AffinityUserInput { + return; + } + + unify( + source: AffinityUserOutput | AffinityUserOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedUserOutput | UnifiedUserOutput[] { + if (!Array.isArray(source)) { + return this.mapSingleUserToUnified(source, customFieldMappings); + } + // Handling array of AffinityUserOutput + return source.map((user) => + this.mapSingleUserToUnified(user, customFieldMappings), + ); + } + + private mapSingleUserToUnified( + user: AffinityUserOutput, + 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: String(user.user.id), + name: `${user.user?.firstName} ${user.user?.lastName}`, + email: user.user?.email, + field_mappings, + }; + } +} diff --git a/packages/api/src/crm/user/services/affinity/types.ts b/packages/api/src/crm/user/services/affinity/types.ts new file mode 100644 index 000000000..d5051de99 --- /dev/null +++ b/packages/api/src/crm/user/services/affinity/types.ts @@ -0,0 +1,27 @@ +interface AffinityUser { + tenant: Tenant + user: User + grant: Grant +} + +interface Tenant { + id: number + name: string + subdomain: string +} + +interface User { + id: number + firstName: string + lastName: string + email: string +} + +interface Grant { + type: string + scope: string + createdAt: string +} + +export type AffinityUserOutput = Partial; +export type AffinityUserInput = null; diff --git a/packages/api/src/crm/user/types/mappingsTypes.ts b/packages/api/src/crm/user/types/mappingsTypes.ts index 2c26f50f4..1f3904ab2 100644 --- a/packages/api/src/crm/user/types/mappingsTypes.ts +++ b/packages/api/src/crm/user/types/mappingsTypes.ts @@ -1,3 +1,4 @@ +import { AffinityUserMapper } from '../services/affinity/mappers'; import { HubspotUserMapper } from '../services/hubspot/mappers'; import { PipedriveUserMapper } from '../services/pipedrive/mappers'; import { ZendeskUserMapper } from '../services/zendesk/mappers'; @@ -10,6 +11,7 @@ const zohoUserMapper = new ZohoUserMapper(); const pipedriveUserMapper = new PipedriveUserMapper(); const closeUserMapper = new CloseUserMapper(); +const affinityUserMapper = new AffinityUserMapper(); export const userUnificationMapping = { hubspot: { unify: hubspotUserMapper.unify.bind(hubspotUserMapper), @@ -31,4 +33,8 @@ export const userUnificationMapping = { unify: closeUserMapper.unify.bind(closeUserMapper), desunify: closeUserMapper.desunify.bind(closeUserMapper), }, + affinity: { + unify: affinityUserMapper.unify.bind(affinityUserMapper), + desunify: affinityUserMapper.desunify.bind(affinityUserMapper), + }, }; diff --git a/packages/api/src/crm/user/user.module.ts b/packages/api/src/crm/user/user.module.ts index 6d251c785..fb5dac694 100644 --- a/packages/api/src/crm/user/user.module.ts +++ b/packages/api/src/crm/user/user.module.ts @@ -1,3 +1,4 @@ +import { AffinityService } from './services/affinity'; import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { SyncService } from './sync/sync.service'; @@ -40,6 +41,7 @@ import { CloseService } from './services/close'; PipedriveService, HubspotService, CloseService, + AffinityService, ], exports: [ SyncService, diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index 7c3a770e9..f3cdfdbe2 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -4,7 +4,8 @@ export enum CrmConnectors { HUBSPOT = 'hubspot', PIPEDRIVE = 'pipedrive', ATTIO = 'attio', - CLOSE = 'close' + CLOSE = 'close', + AFFINITY = 'affinity' } export enum TicketingConnectors { diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index 8de1dda5c..1bd5a81cb 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', 'close']; +export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close', 'affinity']; export const HRIS_PROVIDERS = ['']; export const ATS_PROVIDERS = ['']; export const ACCOUNTING_PROVIDERS = ['']; From eb662c298f0008ef8020482dd6a17517c3ee44bc Mon Sep 17 00:00:00 2001 From: mit-27 Date: Tue, 18 Jun 2024 18:32:12 -0400 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/prisma/schema.prisma | 314 +++++++++++++++++- .../crm/company/services/affinity/index.ts | 6 +- .../crm/company/services/affinity/mappers.ts | 10 +- .../crm/contact/services/affinity/index.ts | 6 +- .../crm/contact/services/affinity/mappers.ts | 10 +- .../src/crm/deal/services/affinity/index.ts | 6 +- .../src/crm/deal/services/affinity/mappers.ts | 8 +- .../src/crm/note/services/affinity/index.ts | 6 +- .../src/crm/note/services/affinity/mappers.ts | 9 +- .../src/crm/user/services/affinity/index.ts | 4 +- .../src/crm/user/services/affinity/mappers.ts | 9 +- 11 files changed, 345 insertions(+), 43 deletions(-) diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 7e528d24a..424e9a9c6 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -649,13 +649,13 @@ model connector_sets { crm_zoho Boolean crm_attio Boolean crm_pipedrive Boolean - crm_zendesk Boolean - crm_close Boolean tcg_zendesk Boolean tcg_jira Boolean tcg_gorgias Boolean tcg_gitlab Boolean tcg_front Boolean + crm_zendesk Boolean + crm_close Boolean projects projects[] } @@ -674,25 +674,26 @@ model managed_webhooks { model fs_drives { id_fs_drive String @id(map: "pk_fs_drives") @db.Uuid - remote_created_at DateTime? @db.Timestamp(6) drive_url String? + name String? + remote_created_at DateTime? @db.Timestamp(6) + remote_id 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? + file_url 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 + id_fs_permission String? @db.Uuid @@index([id_fs_folder], map: "fk_fs_file_folderid") @@index([id_fs_permission], map: "fk_fs_file_permissionid") @@ -702,13 +703,14 @@ model fs_folders { id_fs_folder String @id(map: "pk_fs_folders") @db.Uuid folder_url String? size BigInt? + name String? 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 + id_fs_permission String? @db.Uuid @@index([id_fs_drive], map: "fk_fs_folder_driveid") @@index([id_fs_permission], map: "fk_fs_folder_permissionid") @@ -718,16 +720,304 @@ model fs_folders { 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 + user String? @db.Uuid + group String? @db.Uuid type String[] roles String[] + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) } model fs_shared_links { - id_fs_shared_link String @id(map: "pk_fs_shared_links") @db.Uuid + id_fs_shared_link String @id(map: "pk_fs_shared_links") @db.Uuid + url String? + download_url String? + id_fs_folder String? @db.Uuid + id_fs_file String? @db.Uuid + scope String? + password_protected Boolean + password String? + expires_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) +} + +/// 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 ats_activities { + id_ats_activity String @id(map: "pk_ats_activities") @db.Uuid + activity_type String? + subject String? + body String? + visibility String? + id_ats_candidate String? @db.Uuid + remote_id String? + remote_created_at DateTime? @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + + @@index([id_ats_candidate], map: "fk_activity_candidate") +} + +/// 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 ats_applications { + id_ats_application String @id(map: "pk_ats_applications") @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_candidate String? @db.Uuid + id_ats_job String? @db.Uuid + applied_at DateTime? @db.Timestamp(6) + rejected_at DateTime? @db.Timestamp(6) + offers String[] + source String? + credited_to String? @db.Uuid + current_stage String? @db.Uuid + reject_reason String? + + @@index([id_ats_job], map: "fk_ats_application_ats_job_id") + @@index([id_ats_candidate], map: "fk_ats_application_atscandidateid") +} + +/// 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 ats_candidate_attachments { + id_ats_candidate_attachment String @id(map: "pk_ats_candidate_attachments") @db.Uuid + remote_id String? + file_url String? + file_name String? + remote_created_at DateTime? @db.Timestamp(6) + remote_modified_at DateTime? @db.Timestamp(6) + file_type String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_candidate String @db.Uuid + + @@index([id_ats_candidate], map: "fk_ats_candidate_attachment_candidateid_index") +} + +model ats_candidate_email_addresses { + id_ats_candidate_email_address String @id(map: "pk_ats_candidate_email_addresses") @db.Uuid + value String? + type String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_candidate String @db.Uuid + + @@index([id_ats_candidate], map: "fk_candidate_email_id") +} + +/// 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 ats_candidate_phone_numbers { + id_ats_candidate_phone_number String @id(map: "pk_ats_candidate_phone_numbers") @db.Uuid + value String? + type String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_candidate String @db.Uuid + + @@index([id_ats_candidate], map: "fk_candidate_phone_id") +} + +model ats_candidate_tags { + id_ats_candidate_tag String @id(map: "pk_ats_candidate_tags") @db.Uuid + name String? + id_ats_candidate String? @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + + @@index([id_ats_candidate], map: "fk_candidates_candidatestags") +} + +model ats_candidate_urls { + id_ats_candidate_url String @id(map: "pk_ats_candidate_urls") @db.Uuid + value String? + type String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_candidate String @db.Uuid + + @@index([id_ats_candidate], map: "fk_candidate_url_id") +} + +/// 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 ats_candidates { + id_ats_candidate String @id(map: "pk_ats_candidates") @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + first_name String? + last_name String? + company String? + title String? + remote_created_at DateTime? @db.Timestamp(6) + remote_modified_at DateTime? @db.Timestamp(6) + last_interaction_at DateTime? @db.Timestamp(6) + is_private Boolean? + email_reachable Boolean? + locations String? +} + +model ats_departments { + id_ats_department String @id(map: "pk_ats_departments") @db.Uuid + name String? + remote_id String? created_at DateTime @db.Timestamp(6) modified_at DateTime @db.Timestamp(6) } + +/// 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 ats_eeocs { + id_ats_eeoc String @id(map: "pk_ats_eeocs") @db.Uuid + id_ats_candidate String? @db.Uuid + submitted_at DateTime? @db.Timestamp(6) + race String? + gender String? + veteran_status String? + disability_status String? + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + + @@index([id_ats_candidate], map: "fk_candidate_eeocsid") +} + +/// 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 ats_interviews { + id_ats_interview String @id(map: "pk_ats_interviews") @db.Uuid + status String? + id_ats_application String? @db.Uuid + id_ats_job_interview_stage String? @db.Uuid + organized_by String? @db.Uuid + interviewers String[] + location String? + start_at DateTime? @db.Timestamp(6) + end_at DateTime? @db.Timestamp(6) + remote_created_at DateTime? @db.Timestamp(6) + remote_updated_at DateTime? @db.Timestamp(6) + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + + @@index([id_ats_application], map: "fk_applications_interviews") + @@index([id_ats_job_interview_stage], map: "fk_id_ats_job_interview_stageid") +} + +/// 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 ats_job_interview_stages { + id_ats_job_interview_stage String @id(map: "pk_ats_job_interview_stages") @db.Uuid + name String? + stage_order Int? + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_job String? @db.Uuid + + @@index([id_ats_job], map: "fk_ats_jobs_ats_jobinterview_id") +} + +/// 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 ats_jobs { + id_ats_job String @id(map: "pk_ats_jobs") @db.Uuid + name String? + description String? + code String? + status String? + type String? + confidential Boolean? + ats_departments String[] + ats_offices String[] + managers String[] + recruiters String[] + remote_id String? + remote_created_at DateTime? @db.Timestamp(6) + remote_updated_at DateTime? @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) +} + +/// 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 ats_offers { + id_ats_offer String @id(map: "pk_ats_offers") @db.Uuid + remote_id String? + created_by String? @db.Uuid + remote_created_at DateTime? @db.Timestamp(6) + closed_at DateTime? @db.Timestamp(6) + sent_at DateTime? @db.Timestamp(6) + start_date DateTime? @db.Timestamp(6) + status String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_ats_application String @db.Uuid + + @@index([id_ats_application], map: "fk_ats_offers_applicationid") +} + +/// 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 ats_offices { + id_ats_office String @id(map: "pk_ats_offices") @db.Uuid + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + name String? + location String? +} + +model ats_reject_reasons { + id_ats_reject_reason String @id(map: "pk_ats_reject_reasons") @db.Uuid + name String? + remote_id String? + modified_at DateTime @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) +} + +/// 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 ats_scorecards { + id_ats_scorecard String @id(map: "pk_ats_scorecards") @db.Uuid + overall_recommendation String? + id_ats_application String? @db.Uuid + id_ats_interview String? @db.Uuid + remote_id String? + remote_created_at DateTime? @db.Timestamp(6) + submitted_at DateTime? @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + + @@index([id_ats_application], map: "fk_applications_scorecard") + @@index([id_ats_interview], map: "fk_interviews_scorecards") +} + +/// 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 ats_users { + id_ats_user String @id(map: "pk_ats_users") @db.Uuid + remote_id String? + first_name String? + last_name String? + email String? + disabled Boolean? + access_role String? + remote_created_at DateTime? @db.Timestamp(6) + remote_modified_at DateTime? @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) +} + +/// 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_groups { + id_fs_group String @id(map: "pk_fs_groups") @db.Uuid + name String? + users Json? + remote_id String? + remote_was_deleted Boolean + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) +} + +model fs_users { + id_fs_user String @id(map: "pk_fs_users") @db.Uuid + name String? + email String? + is_me Boolean + remote_id String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) +} diff --git a/packages/api/src/crm/company/services/affinity/index.ts b/packages/api/src/crm/company/services/affinity/index.ts index 70e4ee881..cd5a4d56e 100644 --- a/packages/api/src/crm/company/services/affinity/index.ts +++ b/packages/api/src/crm/company/services/affinity/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 AffinityService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', @@ -96,7 +96,7 @@ export class AffinityService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', diff --git a/packages/api/src/crm/company/services/affinity/mappers.ts b/packages/api/src/crm/company/services/affinity/mappers.ts index d1126c253..f1a6a618e 100644 --- a/packages/api/src/crm/company/services/affinity/mappers.ts +++ b/packages/api/src/crm/company/services/affinity/mappers.ts @@ -5,13 +5,15 @@ import { } from '@crm/company/types/model.unified'; import { ICompanyMapper } from '@crm/company/types'; import { Utils } from '@crm/@lib/@utils'; +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +@Injectable() export class AffinityCompanyMapper implements ICompanyMapper { - private readonly utils: Utils; - - constructor() { - this.utils = new Utils(); + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'company', 'affinity', this); } + async desunify( source: UnifiedCompanyInput, customFieldMappings?: { diff --git a/packages/api/src/crm/contact/services/affinity/index.ts b/packages/api/src/crm/contact/services/affinity/index.ts index 7ed4ab6bc..441a57d5f 100644 --- a/packages/api/src/crm/contact/services/affinity/index.ts +++ b/packages/api/src/crm/contact/services/affinity/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 AffinityService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', @@ -103,7 +103,7 @@ export class AffinityService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', diff --git a/packages/api/src/crm/contact/services/affinity/mappers.ts b/packages/api/src/crm/contact/services/affinity/mappers.ts index 478e6ca74..e83ed42d5 100644 --- a/packages/api/src/crm/contact/services/affinity/mappers.ts +++ b/packages/api/src/crm/contact/services/affinity/mappers.ts @@ -6,12 +6,14 @@ import { import { IContactMapper } from '@crm/contact/types'; import { AffinityContactInput, AffinityContactOutput } from './types'; import { Utils } from '@crm/@lib/@utils'; +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; -export class AffinityContactMapper implements IContactMapper { - private readonly utils: Utils; - constructor() { - this.utils = new Utils(); +@Injectable() +export class AffinityContactMapper implements IContactMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'contact', 'affinity', this); } async desunify( diff --git a/packages/api/src/crm/deal/services/affinity/index.ts b/packages/api/src/crm/deal/services/affinity/index.ts index 2c5f4934d..26eab7c2a 100644 --- a/packages/api/src/crm/deal/services/affinity/index.ts +++ b/packages/api/src/crm/deal/services/affinity/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'; @@ -73,7 +73,7 @@ export class AffinityService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', @@ -116,7 +116,7 @@ export class AffinityService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', diff --git a/packages/api/src/crm/deal/services/affinity/mappers.ts b/packages/api/src/crm/deal/services/affinity/mappers.ts index 6998a84d8..c851b0ad6 100644 --- a/packages/api/src/crm/deal/services/affinity/mappers.ts +++ b/packages/api/src/crm/deal/services/affinity/mappers.ts @@ -5,12 +5,12 @@ import { } from '@crm/deal/types/model.unified'; import { IDealMapper } from '@crm/deal/types'; import { Utils } from '@crm/@lib/@utils'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; export class AffinityDealMapper implements IDealMapper { - private readonly utils: Utils; - - constructor() { - this.utils = new Utils(); + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'deal', 'affinity', this); } async desunify( diff --git a/packages/api/src/crm/note/services/affinity/index.ts b/packages/api/src/crm/note/services/affinity/index.ts index f396996b2..9ea5f679e 100644 --- a/packages/api/src/crm/note/services/affinity/index.ts +++ b/packages/api/src/crm/note/services/affinity/index.ts @@ -5,7 +5,7 @@ import { AffinityNoteInput, AffinityNoteOutput } 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 AffinityService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', @@ -93,7 +93,7 @@ export class AffinityService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', diff --git a/packages/api/src/crm/note/services/affinity/mappers.ts b/packages/api/src/crm/note/services/affinity/mappers.ts index 34d1d724c..71f93d48c 100644 --- a/packages/api/src/crm/note/services/affinity/mappers.ts +++ b/packages/api/src/crm/note/services/affinity/mappers.ts @@ -5,12 +5,13 @@ import { } from '@crm/note/types/model.unified'; import { INoteMapper } from '@crm/note/types'; import { Utils } from '@crm/@lib/@utils'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; +@Injectable() export class AffinityNoteMapper implements INoteMapper { - private readonly utils: Utils; - - constructor() { - this.utils = new Utils(); + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'note', 'affinity', this); } async desunify( diff --git a/packages/api/src/crm/user/services/affinity/index.ts b/packages/api/src/crm/user/services/affinity/index.ts index 745b4c433..16288a0be 100644 --- a/packages/api/src/crm/user/services/affinity/index.ts +++ b/packages/api/src/crm/user/services/affinity/index.ts @@ -5,7 +5,7 @@ import { AffinityUserOutput } 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'; @@ -56,7 +56,7 @@ export class AffinityService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Affinity', diff --git a/packages/api/src/crm/user/services/affinity/mappers.ts b/packages/api/src/crm/user/services/affinity/mappers.ts index 4e7afe0e0..cdecba638 100644 --- a/packages/api/src/crm/user/services/affinity/mappers.ts +++ b/packages/api/src/crm/user/services/affinity/mappers.ts @@ -4,8 +4,15 @@ import { UnifiedUserOutput, } from '@crm/user/types/model.unified'; import { IUserMapper } from '@crm/user/types'; - +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; +import { Utils } from '@crm/@lib/@utils'; export class AffinityUserMapper implements IUserMapper { + + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'user', 'affinity', this); + } + desunify( source: UnifiedUserInput, customFieldMappings?: { From 9e8abddc6e57c4f0ed97531cf793fdef48df3263 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Fri, 6 Sep 2024 15:43:39 -0600 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20Update=20all=20objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/scripts/init.sql | 1 + packages/api/scripts/seed.sql | 8 +- .../utils/types/original/original.crm.ts | 10 +- .../api/src/crm/company/company.module.ts | 4 + .../crm/company/services/affinity/index.ts | 31 ++--- .../crm/company/services/affinity/mappers.ts | 30 +++-- .../api/src/crm/contact/contact.module.ts | 4 + .../crm/contact/services/affinity/index.ts | 34 ++---- .../crm/contact/services/affinity/mappers.ts | 40 +++--- packages/api/src/crm/deal/deal.module.ts | 4 + .../src/crm/deal/services/affinity/index.ts | 41 +++---- .../src/crm/deal/services/affinity/mappers.ts | 32 ++--- packages/api/src/crm/note/note.module.ts | 4 + .../src/crm/note/services/affinity/index.ts | 40 +++--- .../src/crm/note/services/affinity/mappers.ts | 25 ++-- .../src/crm/user/services/affinity/index.ts | 35 +++--- .../src/crm/user/services/affinity/mappers.ts | 36 +++--- packages/api/src/crm/user/user.module.ts | 4 + packages/api/swagger/swagger-spec.yaml | 115 ++++++++++++++++-- packages/shared/src/connectors/enum.ts | 1 + packages/shared/src/connectors/index.ts | 2 +- 21 files changed, 294 insertions(+), 207 deletions(-) diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 4d58b5ffa..6a714c7a4 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -552,6 +552,7 @@ CREATE TABLE connector_sets ats_ashby boolean NULL, ecom_webflow boolean NULL, crm_microsoftdynamicssales boolean NULL, + crm_affinity boolean NULL, CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 22df9b0bf..d727bb268 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_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow, tcg_linear, ecom_shopify, ecom_woocommerce, ecom_amazon, ecom_squarespace, hris_gusto) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, 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, TRUE, TRUE, TRUE, 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, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow, tcg_linear, ecom_shopify, ecom_woocommerce, ecom_amazon, ecom_squarespace, hris_gusto, crm_affinity) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, 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', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), 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 bd0e9b62f..5258c1532 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -1,4 +1,5 @@ + import { MicrosoftdynamicssalesEngagementInput, MicrosoftdynamicssalesEngagementOutput } from '@crm/engagement/services/microsoftdynamicssales/types'; import { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } from '@crm/task/services/microsoftdynamicssales/types'; @@ -153,12 +154,17 @@ import { ZendeskUserInput, ZendeskUserOutput, } from '@ticketing/user/services/zendesk/types'; +import { AffinityCompanyInput, AffinityCompanyOutput } from '@crm/company/services/affinity/types'; +import { AffinityDealInput, AffinityDealOutput } from '@crm/deal/services/affinity/types'; +import { AffinityNoteInput, AffinityNoteOutput } from '@crm/note/services/affinity/types'; +import { AffinityUserInput, AffinityUserOutput } from '@crm/user/services/affinity/types'; +import { AffinityContactInput, AffinityContactOutput } from '@crm/contact/services/affinity/types'; /* INPUT */ /* contact */ export type OriginalContactInput = - | AffinityContactInput + | AffinityCompanyInput | HubspotContactInput | ZohoContactInput | ZendeskContactInput @@ -245,7 +251,7 @@ export type CrmObjectInput = /* OUTPUT */ export type OriginalContactOutput = - | AffinityContactOutput + | AffinityContactInput | HubspotContactOutput | ZohoContactOutput | ZendeskContactOutput diff --git a/packages/api/src/crm/company/company.module.ts b/packages/api/src/crm/company/company.module.ts index 1d3e61e70..568eee7aa 100644 --- a/packages/api/src/crm/company/company.module.ts +++ b/packages/api/src/crm/company/company.module.ts @@ -1,3 +1,5 @@ +import { AffinityCompanyMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; import { MicrosoftdynamicssalesCompanyMapper } from './services/microsoftdynamicssales/mappers'; import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; @@ -57,6 +59,8 @@ import { SyncService } from './sync/sync.service'; ZohoCompanyMapper, MicrosoftdynamicssalesService, MicrosoftdynamicssalesCompanyMapper, + AffinityService, + AffinityCompanyMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/company/services/affinity/index.ts b/packages/api/src/crm/company/services/affinity/index.ts index cd5a4d56e..c0ab83a49 100644 --- a/packages/api/src/crm/company/services/affinity/index.ts +++ b/packages/api/src/crm/company/services/affinity/index.ts @@ -2,14 +2,16 @@ import { Injectable } from '@nestjs/common'; 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 { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/encryption/encryption.service'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ICompanyService } from '@crm/company/types'; import { ServiceRegistry } from '../registry.service'; import { AffinityCompanyInput, AffinityCompanyOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; +import { OriginalCompanyOutput } from '@@core/utils/types/original/original.crm'; @Injectable() export class AffinityService implements ICompanyService { @@ -24,7 +26,6 @@ export class AffinityService implements ICompanyService { ); this.registry.registerService('affinity', this); } - async addCompany( companyData: AffinityCompanyInput, linkedUserId: string, @@ -58,20 +59,14 @@ export class AffinityService implements ICompanyService { statusCode: 201, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.company, - ActionType.POST, - ); + throw error; } } - async syncCompanies( - linkedUserId: string, - ): Promise> { + async sync(data: SyncParam): Promise> { try { + const { linkedUserId } = data; + const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -96,13 +91,7 @@ export class AffinityService implements ICompanyService { statusCode: 200, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.company, - ActionType.GET, - ); + throw error; } } } diff --git a/packages/api/src/crm/company/services/affinity/mappers.ts b/packages/api/src/crm/company/services/affinity/mappers.ts index f1a6a618e..6a55052b0 100644 --- a/packages/api/src/crm/company/services/affinity/mappers.ts +++ b/packages/api/src/crm/company/services/affinity/mappers.ts @@ -1,21 +1,21 @@ import { AffinityCompanyInput, AffinityCompanyOutput } from './types'; import { - UnifiedCompanyInput, - UnifiedCompanyOutput, + UnifiedCrmCompanyInput, + UnifiedCrmCompanyOutput, } from '@crm/company/types/model.unified'; import { ICompanyMapper } from '@crm/company/types'; import { Utils } from '@crm/@lib/@utils'; import { Injectable } from '@nestjs/common'; -import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { getCountryCode, getCountryName } from '@@core/utils/types'; @Injectable() export class AffinityCompanyMapper implements ICompanyMapper { constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { this.mappersRegistry.registerService('crm', 'company', 'affinity', this); } - async desunify( - source: UnifiedCompanyInput, + source: UnifiedCrmCompanyInput, customFieldMappings?: { slug: string; remote_id: string; @@ -44,29 +44,39 @@ export class AffinityCompanyMapper implements ICompanyMapper { async unify( source: AffinityCompanyOutput | AffinityCompanyOutput[], + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { if (!Array.isArray(source)) { - return this.mapSingleCompanyToUnified(source, customFieldMappings); + return this.mapSingleCompanyToUnified( + source, + connectionId, + customFieldMappings, + ); } // Handling array of AffinityCompanyOutput return Promise.all( source.map((company) => - this.mapSingleCompanyToUnified(company, customFieldMappings), + this.mapSingleCompanyToUnified( + company, + connectionId, + customFieldMappings, + ), ), ); } private async mapSingleCompanyToUnified( company: AffinityCompanyOutput, + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { @@ -76,8 +86,6 @@ export class AffinityCompanyMapper implements ICompanyMapper { let opts: any = {}; - - return { remote_id: company.id, name: company.name, diff --git a/packages/api/src/crm/contact/contact.module.ts b/packages/api/src/crm/contact/contact.module.ts index e0632e41c..8717e2cfc 100644 --- a/packages/api/src/crm/contact/contact.module.ts +++ b/packages/api/src/crm/contact/contact.module.ts @@ -1,3 +1,5 @@ +import { AffinityContactMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; import { MicrosoftdynamicssalesContactMapper } from './services/microsoftdynamicssales/mappers'; import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; @@ -50,6 +52,8 @@ import { SyncService } from './sync/sync.service'; ZohoContactMapper, MicrosoftdynamicssalesService, MicrosoftdynamicssalesContactMapper, + AffinityService, + AffinityContactMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/contact/services/affinity/index.ts b/packages/api/src/crm/contact/services/affinity/index.ts index 441a57d5f..b299d001b 100644 --- a/packages/api/src/crm/contact/services/affinity/index.ts +++ b/packages/api/src/crm/contact/services/affinity/index.ts @@ -2,13 +2,14 @@ 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 { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/encryption/encryption.service'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; import { AffinityContactInput, AffinityContactOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; @Injectable() export class AffinityService implements IContactService { @@ -57,21 +58,14 @@ export class AffinityService implements IContactService { statusCode: 201, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.contact, - ActionType.POST, - ); + throw error; } - return; } - async syncContacts( - linkedUserId: string, - ): Promise> { + async sync(data: SyncParam): Promise> { try { + const { linkedUserId } = data; + const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -79,8 +73,6 @@ export class AffinityService implements IContactService { vertical: 'crm', }, }); - // console.log('Before Axios'); - // console.log(this.cryptoService.decrypt(connection.access_token)); const resp = await axios.get( `${connection.account_url}/persons`, @@ -95,21 +87,13 @@ export class AffinityService implements IContactService { }, ); - console.log('After Axios'); - return { data: resp.data, message: 'Affinity contacts retrieved', statusCode: 200, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.contact, - ActionType.GET, - ); + throw error; } } } diff --git a/packages/api/src/crm/contact/services/affinity/mappers.ts b/packages/api/src/crm/contact/services/affinity/mappers.ts index e83ed42d5..02d36a439 100644 --- a/packages/api/src/crm/contact/services/affinity/mappers.ts +++ b/packages/api/src/crm/contact/services/affinity/mappers.ts @@ -1,14 +1,13 @@ -import { Address } from '@crm/@lib/@types'; import { - UnifiedContactInput, - UnifiedContactOutput, + UnifiedCrmContactInput, + UnifiedCrmContactOutput, } from '@crm/contact/types/model.unified'; import { IContactMapper } from '@crm/contact/types'; import { AffinityContactInput, AffinityContactOutput } from './types'; import { Utils } from '@crm/@lib/@utils'; import { Injectable } from '@nestjs/common'; -import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; - +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { getCountryCode, getCountryName } from '@@core/utils/types'; @Injectable() export class AffinityContactMapper implements IContactMapper { @@ -17,7 +16,7 @@ export class AffinityContactMapper implements IContactMapper { } async desunify( - source: UnifiedContactInput, + source: UnifiedCrmContactInput, customFieldMappings?: { slug: string; remote_id: string; @@ -30,11 +29,8 @@ export class AffinityContactMapper implements IContactMapper { first_name: source.first_name, last_name: source.last_name, emails: source.email_addresses?.map((e) => e.email_address), - }; - - if (customFieldMappings && source.field_mappings) { for (const [k, v] of Object.entries(source.field_mappings)) { const mapping = customFieldMappings.find( @@ -50,43 +46,47 @@ export class AffinityContactMapper implements IContactMapper { async unify( source: AffinityContactOutput | AffinityContactOutput[], + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { if (!Array.isArray(source)) { - return await this.mapSingleContactToUnified(source, customFieldMappings); + return await this.mapSingleContactToUnified( + source, + connectionId, + customFieldMappings, + ); } // Handling array of HubspotContactOutput return Promise.all( source.map((contact) => - this.mapSingleContactToUnified(contact, customFieldMappings), + this.mapSingleContactToUnified( + contact, + connectionId, + customFieldMappings, + ), ), ); } private async mapSingleContactToUnified( contact: AffinityContactOutput, + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { field_mappings[mapping.slug] = contact[mapping.remote_id]; } } - // const address: Address = { - // street_1: '', - // city: '', - // state: '', - // postal_code: '', - // country: '', - // }; + const opts: any = {}; return { diff --git a/packages/api/src/crm/deal/deal.module.ts b/packages/api/src/crm/deal/deal.module.ts index 6feb8643b..a87bf8c7b 100644 --- a/packages/api/src/crm/deal/deal.module.ts +++ b/packages/api/src/crm/deal/deal.module.ts @@ -1,3 +1,5 @@ +import { AffinityDealMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; import { MicrosoftdynamicssalesDealMapper } from './services/microsoftdynamicssales/mappers'; import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; @@ -48,6 +50,8 @@ import { SyncService } from './sync/sync.service'; CloseDealMapper, MicrosoftdynamicssalesService, MicrosoftdynamicssalesDealMapper, + AffinityService, + AffinityDealMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/deal/services/affinity/index.ts b/packages/api/src/crm/deal/services/affinity/index.ts index 26eab7c2a..488407b88 100644 --- a/packages/api/src/crm/deal/services/affinity/index.ts +++ b/packages/api/src/crm/deal/services/affinity/index.ts @@ -1,14 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ 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, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/encryption/encryption.service'; +import { CrmObject } from '@crm/@lib/@types'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; +import { IDealService } from '@crm/deal/types'; import { ServiceRegistry } from '../registry.service'; import { AffinityDealInput, AffinityDealOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; +import { OriginalDealOutput } from '@@core/utils/types/original/original.crm'; + @Injectable() export class AffinityService implements IDealService { constructor( @@ -73,22 +76,14 @@ export class AffinityService implements IDealService { statusCode: 201, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.deal, - ActionType.POST, - ); + throw error; } } - async syncDeals( - linkedUserId: string, - custom_properties?: string[], - ): Promise> { + async sync(data: SyncParam): Promise> { try { - //crm.schemas.deals.read","crm.objects.deals.read + const { linkedUserId } = data; + const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -96,8 +91,6 @@ export class AffinityService implements IDealService { vertical: 'crm', }, }); - - const baseURL = `${connection.account_url}/opportunity/`; const resp = await axios.get( `${connection.account_url}/opportunities`, { @@ -116,13 +109,7 @@ export class AffinityService implements IDealService { statusCode: 200, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.deal, - ActionType.GET, - ); + throw error; } } } diff --git a/packages/api/src/crm/deal/services/affinity/mappers.ts b/packages/api/src/crm/deal/services/affinity/mappers.ts index c851b0ad6..0ca68cd28 100644 --- a/packages/api/src/crm/deal/services/affinity/mappers.ts +++ b/packages/api/src/crm/deal/services/affinity/mappers.ts @@ -1,20 +1,21 @@ -import { AffinityDealInput, AffinityDealOutput } from './types'; +import { Injectable } from '@nestjs/common'; import { - UnifiedDealInput, - UnifiedDealOutput, + UnifiedCrmDealInput, + UnifiedCrmDealOutput, } from '@crm/deal/types/model.unified'; import { IDealMapper } from '@crm/deal/types'; import { Utils } from '@crm/@lib/@utils'; -import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; -import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { AffinityDealInput, AffinityDealOutput } from './types'; +@Injectable() export class AffinityDealMapper implements IDealMapper { constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { this.mappersRegistry.registerService('crm', 'deal', 'affinity', this); } async desunify( - source: UnifiedDealInput, + source: UnifiedCrmDealInput, customFieldMappings?: { slug: string; remote_id: string; @@ -37,7 +38,6 @@ export class AffinityDealMapper implements IDealMapper { name: source.name, list_id: 21, ...opts - }; if (customFieldMappings && source.field_mappings) { @@ -50,34 +50,41 @@ export class AffinityDealMapper implements IDealMapper { } } } + return result; } async unify( source: AffinityDealOutput | AffinityDealOutput[], + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { if (!Array.isArray(source)) { - return await this.mapSingleDealToUnified(source, customFieldMappings); + return await this.mapSingleDealToUnified( + source, + connectionId, + customFieldMappings, + ); } // Handling array of AffinityDealOutput return Promise.all( source.map((deal) => - this.mapSingleDealToUnified(deal, customFieldMappings), + this.mapSingleDealToUnified(deal, connectionId, customFieldMappings), ), ); } private async mapSingleDealToUnified( deal: AffinityDealOutput, + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { @@ -114,9 +121,6 @@ export class AffinityDealMapper implements IDealMapper { } } - - - return { remote_id: String(deal.id), name: deal.name, diff --git a/packages/api/src/crm/note/note.module.ts b/packages/api/src/crm/note/note.module.ts index 58d1250e0..ae4d50733 100644 --- a/packages/api/src/crm/note/note.module.ts +++ b/packages/api/src/crm/note/note.module.ts @@ -1,3 +1,5 @@ +import { AffinityNoteMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; import { MicrosoftdynamicssalesNoteMapper } from './services/microsoftdynamicssales/mappers'; import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; @@ -47,6 +49,8 @@ import { SyncService } from './sync/sync.service'; CloseNoteMapper, MicrosoftdynamicssalesService, MicrosoftdynamicssalesNoteMapper, + AffinityService, + AffinityNoteMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/note/services/affinity/index.ts b/packages/api/src/crm/note/services/affinity/index.ts index 9ea5f679e..e10b1e12e 100644 --- a/packages/api/src/crm/note/services/affinity/index.ts +++ b/packages/api/src/crm/note/services/affinity/index.ts @@ -1,14 +1,14 @@ -import { Injectable } from '@nestjs/common'; -import { INoteService } from '@crm/note/types'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { ApiResponse } from '@@core/utils/types'; +import { SyncParam } from '@@core/utils/types/interface'; import { CrmObject } from '@crm/@lib/@types'; -import { AffinityNoteInput, AffinityNoteOutput } from './types'; +import { INoteService } from '@crm/note/types'; +import { Injectable } from '@nestjs/common'; import axios from 'axios'; -import { PrismaService } from '@@core/prisma/prisma.service'; -import { LoggerService } from '@@core/logger/logger.service'; -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'; +import { AffinityNoteInput, AffinityNoteOutput } from './types'; @Injectable() export class AffinityService implements INoteService { @@ -23,6 +23,7 @@ export class AffinityService implements INoteService { ); this.registry.registerService('affinity', this); } + async addNote( noteData: AffinityNoteInput, linkedUserId: string, @@ -53,21 +54,14 @@ export class AffinityService implements INoteService { statusCode: 201, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.note, - ActionType.POST, - ); + throw error; } } - async syncNotes( - linkedUserId: string, - custom_properties?: string[], - ): Promise> { + async sync(data: SyncParam): Promise> { try { + const { linkedUserId } = data; + const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -93,13 +87,7 @@ export class AffinityService implements INoteService { statusCode: 200, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.note, - ActionType.GET, - ); + throw error; } } } diff --git a/packages/api/src/crm/note/services/affinity/mappers.ts b/packages/api/src/crm/note/services/affinity/mappers.ts index 71f93d48c..d36752c7f 100644 --- a/packages/api/src/crm/note/services/affinity/mappers.ts +++ b/packages/api/src/crm/note/services/affinity/mappers.ts @@ -1,11 +1,11 @@ import { AffinityNoteInput, AffinityNoteOutput } from './types'; import { - UnifiedNoteInput, - UnifiedNoteOutput, + UnifiedCrmNoteInput, + UnifiedCrmNoteOutput, } from '@crm/note/types/model.unified'; import { INoteMapper } from '@crm/note/types'; import { Utils } from '@crm/@lib/@utils'; -import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -13,16 +13,13 @@ export class AffinityNoteMapper implements INoteMapper { constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { this.mappersRegistry.registerService('crm', 'note', 'affinity', this); } - async desunify( - source: UnifiedNoteInput, + source: UnifiedCrmNoteInput, customFieldMappings?: { slug: string; remote_id: string; }[], ): Promise { - - let opts: any = {}; if (source.user_id) { @@ -86,29 +83,35 @@ export class AffinityNoteMapper implements INoteMapper { async unify( source: AffinityNoteOutput | AffinityNoteOutput[], + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { if (!Array.isArray(source)) { - return await this.mapSingleNoteToUnified(source, customFieldMappings); + return await this.mapSingleNoteToUnified( + source, + connectionId, + customFieldMappings, + ); } return Promise.all( source.map((note) => - this.mapSingleNoteToUnified(note, customFieldMappings), + this.mapSingleNoteToUnified(note, connectionId, customFieldMappings), ), ); } private async mapSingleNoteToUnified( note: AffinityNoteOutput, + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { diff --git a/packages/api/src/crm/user/services/affinity/index.ts b/packages/api/src/crm/user/services/affinity/index.ts index 16288a0be..a150e6b92 100644 --- a/packages/api/src/crm/user/services/affinity/index.ts +++ b/packages/api/src/crm/user/services/affinity/index.ts @@ -1,14 +1,14 @@ -import { Injectable } from '@nestjs/common'; -import { IUserService } from '@crm/user/types'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { ApiResponse } from '@@core/utils/types'; +import { SyncParam } from '@@core/utils/types/interface'; import { CrmObject } from '@crm/@lib/@types'; -import { AffinityUserOutput } from './types'; +import { IUserService } from '@crm/user/types'; +import { Injectable } from '@nestjs/common'; import axios from 'axios'; -import { PrismaService } from '@@core/prisma/prisma.service'; -import { LoggerService } from '@@core/logger/logger.service'; -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'; +import { AffinityUserOutput } from './types'; @Injectable() export class AffinityService implements IUserService { @@ -21,18 +21,17 @@ export class AffinityService implements IUserService { this.logger.setContext( CrmObject.user.toUpperCase() + ':' + AffinityService.name, ); - this.registry.registerService('close', this); + this.registry.registerService('affinity', this); } - async syncUsers( - linkedUserId: string, - custom_properties?: string[], - ): Promise> { + async sync(data: SyncParam): Promise> { try { + const { linkedUserId } = data; + const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, - provider_slug: 'close', + provider_slug: 'affinity', vertical: 'crm', }, }); @@ -56,13 +55,7 @@ export class AffinityService implements IUserService { statusCode: 200, }; } catch (error) { - handle3rdPartyServiceError( - error, - this.logger, - 'Affinity', - CrmObject.user, - ActionType.GET, - ); + throw error; } } } diff --git a/packages/api/src/crm/user/services/affinity/mappers.ts b/packages/api/src/crm/user/services/affinity/mappers.ts index cdecba638..d9a3aaa43 100644 --- a/packages/api/src/crm/user/services/affinity/mappers.ts +++ b/packages/api/src/crm/user/services/affinity/mappers.ts @@ -1,51 +1,59 @@ -import { AffinityUserInput, AffinityUserOutput } from './types'; +import { AffinityUserOutput } from './types'; import { - UnifiedUserInput, - UnifiedUserOutput, + UnifiedCrmUserInput, + UnifiedCrmUserOutput, } from '@crm/user/types/model.unified'; import { IUserMapper } from '@crm/user/types'; -import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; import { Injectable } from '@nestjs/common'; import { Utils } from '@crm/@lib/@utils'; -export class AffinityUserMapper implements IUserMapper { +@Injectable() +export class AffinityUserMapper implements IUserMapper { constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { this.mappersRegistry.registerService('crm', 'user', 'affinity', this); } - desunify( - source: UnifiedUserInput, + source: UnifiedCrmUserInput, customFieldMappings?: { slug: string; remote_id: string; }[], - ): AffinityUserInput { + ) { return; } unify( source: AffinityUserOutput | AffinityUserOutput[], + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): UnifiedUserOutput | UnifiedUserOutput[] { + ): Promise { if (!Array.isArray(source)) { - return this.mapSingleUserToUnified(source, customFieldMappings); + return this.mapSingleUserToUnified( + source, + connectionId, + customFieldMappings, + ); } // Handling array of AffinityUserOutput - return source.map((user) => - this.mapSingleUserToUnified(user, customFieldMappings), + return Promise.all( + source.map((user) => + this.mapSingleUserToUnified(user, connectionId, customFieldMappings), + ), ); } - private mapSingleUserToUnified( + private async mapSingleUserToUnified( user: AffinityUserOutput, + connectionId: string, customFieldMappings?: { slug: string; remote_id: string; }[], - ): UnifiedUserOutput { + ): Promise { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { diff --git a/packages/api/src/crm/user/user.module.ts b/packages/api/src/crm/user/user.module.ts index afe940154..578c2adaf 100644 --- a/packages/api/src/crm/user/user.module.ts +++ b/packages/api/src/crm/user/user.module.ts @@ -1,3 +1,5 @@ +import { AffinityUserMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; import { MicrosoftdynamicssalesUserMapper } from './services/microsoftdynamicssales/mappers'; import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; @@ -47,6 +49,8 @@ import { UserController } from './user.controller'; CloseUserMapper, MicrosoftdynamicssalesService, MicrosoftdynamicssalesUserMapper, + AffinityService, + AffinityUserMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 01549aaab..6a58b8106 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -107,6 +107,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -127,6 +129,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -181,6 +185,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -305,6 +310,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -396,6 +402,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -485,6 +492,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -620,6 +628,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -744,6 +753,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -868,6 +878,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -991,6 +1002,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1115,6 +1127,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1239,6 +1252,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1330,6 +1344,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1453,6 +1468,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1544,6 +1560,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1636,6 +1653,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1763,6 +1781,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1854,6 +1873,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2153,11 +2173,15 @@ paths: required: false in: query schema: + minimum: 1 + default: 1 type: number - name: limit required: false in: query schema: + minimum: 1 + default: 10 type: number responses: '200': @@ -2193,6 +2217,12 @@ paths: application/json: schema: type: object + '201': + description: '' + content: + application/json: + schema: + type: object tags: &ref_21 - passthrough x-speakeasy-group: passthrough @@ -2238,6 +2268,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2329,6 +2360,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2420,6 +2452,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2511,6 +2544,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2602,6 +2636,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2694,6 +2729,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2817,6 +2853,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2909,6 +2946,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3000,6 +3038,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3091,6 +3130,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3182,6 +3222,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3273,6 +3314,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3364,6 +3406,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3487,6 +3530,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3578,6 +3622,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3701,6 +3746,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3830,6 +3876,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3961,6 +4008,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4090,6 +4138,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4219,6 +4268,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4313,6 +4363,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4407,6 +4458,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4535,6 +4587,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4629,6 +4682,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4757,6 +4811,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4851,6 +4906,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4975,6 +5031,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5099,6 +5156,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5223,6 +5281,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5347,6 +5406,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5438,6 +5498,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5562,6 +5623,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5654,6 +5716,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5745,6 +5808,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5836,6 +5900,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5927,6 +5992,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6018,6 +6084,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6109,6 +6176,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6200,6 +6268,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6291,6 +6360,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6380,6 +6450,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6504,6 +6575,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6595,6 +6667,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6720,6 +6793,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6812,6 +6886,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6904,6 +6979,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6996,6 +7072,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7120,6 +7197,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7212,6 +7290,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7336,6 +7415,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7428,6 +7508,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7552,6 +7633,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7643,6 +7725,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7768,6 +7851,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7892,6 +7976,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7984,6 +8069,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8109,6 +8195,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8200,6 +8287,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8292,6 +8380,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8384,6 +8473,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8476,6 +8566,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8567,6 +8658,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8691,6 +8783,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8815,6 +8908,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8906,6 +9000,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8997,6 +9092,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9119,6 +9215,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9241,6 +9338,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9330,6 +9428,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9420,6 +9519,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9550,7 +9650,6 @@ components: password_hash: type: string required: - - id_user - email - password_hash Connection: @@ -9638,8 +9737,8 @@ components: description: The unique UUID of the webhook. endpoint_description: type: string - example: Webhook to receive connection events nullable: true + example: Webhook to receive connection events description: The description of the webhook. url: type: string @@ -9677,8 +9776,8 @@ components: last_update: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The last update date of the webhook. required: - id_webhook_endpoint @@ -9713,7 +9812,6 @@ components: type: string required: - url - - description - scope SignatureVerificationDto: type: object @@ -11653,8 +11751,6 @@ components: - id_project - name - sync_mode - - pull_frequency - - redirect_url - id_user - id_connector_set CreateProjectDto: @@ -12023,10 +12119,10 @@ components: type: object properties: method: - type: string enum: - GET - POST + type: string path: type: string nullable: true @@ -12045,12 +12141,11 @@ components: type: object additionalProperties: true nullable: true + headers: + type: object required: - method - path - - data - - request_format - - overrideBaseUrl UnifiedHrisBankinfoOutput: type: object properties: diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index 8067be7a8..aa4d59f1d 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -5,6 +5,7 @@ export enum CrmConnectors { PIPEDRIVE = 'pipedrive', ATTIO = 'attio', CLOSE = 'close', + AFFINITY = 'affinity', MICROSOFTDYNAMICSSALES = 'microsoftdynamicssales' } diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index d06c9a292..4785ba09a 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', 'close', 'microsoftdynamicssales']; +export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close', 'microsoftdynamicssales', 'affinity']; export const HRIS_PROVIDERS = []; export const ATS_PROVIDERS = ['ashby']; export const ACCOUNTING_PROVIDERS = []; From 9cbd82316ae922818d58667c338421e235229f63 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Fri, 13 Sep 2024 18:32:22 -0600 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=92=9A=20Fix=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/scripts/init.sql | 1 + packages/api/scripts/seed.sql | 8 ++++---- .../@core/utils/types/original/original.crm.ts | 18 +++++++++++------- packages/api/src/crm/company/company.module.ts | 4 +++- packages/api/src/crm/contact/contact.module.ts | 4 +++- packages/api/src/crm/deal/deal.module.ts | 4 +++- packages/api/src/crm/note/note.module.ts | 4 +++- packages/shared/src/connectors/enum.ts | 1 + 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 43efca349..dfc584060 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -556,6 +556,7 @@ CREATE TABLE connector_sets fs_googledrive boolean NULL, fs_sharepoint boolean NULL, fs_onedrive boolean NULL, + crm_affinity boolean NULL, CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index dc087a70d..45705aff7 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -2,10 +2,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first ('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_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow, tcg_linear, ecom_shopify, ecom_woocommerce, ecom_amazon, ecom_squarespace, hris_gusto, fs_googledrive, fs_dropbox, fs_sharepoint, fs_onedrive) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow, tcg_linear, ecom_shopify, ecom_woocommerce, ecom_amazon, ecom_squarespace, hris_gusto, fs_googledrive, fs_dropbox, fs_sharepoint, fs_onedrive,crm_affinity) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), 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 5297d2fca..081f68356 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -1,5 +1,9 @@ - +import { AffinityCompanyInput, AffinityCompanyOutput } from '@crm/company/services/affinity/types'; +import { AffinityDealInput, AffinityDealOutput } from '@crm/deal/services/affinity/types'; +import { AffinityNoteInput, AffinityNoteOutput } from '@crm/note/services/affinity/types'; +import { AffinityUserInput, AffinityUserOutput } from '@crm/user/services/affinity/types'; +import { AffinityContactInput, AffinityContactOutput } from '@crm/contact/services/affinity/types'; import { MicrosoftdynamicssalesEngagementInput, MicrosoftdynamicssalesEngagementOutput } from '@crm/engagement/services/microsoftdynamicssales/types'; import { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } from '@crm/task/services/microsoftdynamicssales/types'; @@ -172,7 +176,7 @@ export type OriginalContactInput = | ZendeskContactInput | PipedriveContactInput | AttioContactInput - | CloseContactInput + | CloseContactInput | MicrosoftdynamicssalesContactInput | SalesforceContactInput; @@ -184,7 +188,7 @@ export type OriginalDealInput = | ZendeskDealOutput | PipedriveDealOutput | CloseDealOutput - | AttioDealInput + | AttioDealInput | MicrosoftdynamicssalesDealInput | SalesforceDealInput @@ -223,7 +227,7 @@ export type OriginalTaskInput = | ZendeskTaskInput | PipedriveTaskInput | CloseTaskInput - | AttioTaskInput | MicrosoftdynamicssalesTaskInput | SalesforceTaskInput; + | AttioTaskInput | MicrosoftdynamicssalesTaskInput | SalesforceTaskInput; /* stage */ export type OriginalStageInput = @@ -242,7 +246,7 @@ export type OriginalUserInput = | ZohoUserInput | ZendeskUserInput | PipedriveUserInput - | CloseUserOutput | MicrosoftdynamicssalesUserInput | SalesforceUserInput + | CloseUserOutput | MicrosoftdynamicssalesUserInput | SalesforceUserInput export type CrmObjectInput = | OriginalContactInput @@ -301,7 +305,7 @@ export type OriginalNoteOutput = | ZendeskNoteOutput | PipedriveNoteOutput | CloseNoteOutput - | AttioNoteOutput | MicrosoftdynamicssalesNoteOutput | SalesforceNoteOutput; + | AttioNoteOutput | MicrosoftdynamicssalesNoteOutput | SalesforceNoteOutput; /* task */ export type OriginalTaskOutput = @@ -330,7 +334,7 @@ export type OriginalUserOutput = | ZendeskUserOutput | PipedriveUserOutput | CloseUserInput - | AttioUserOutput | MicrosoftdynamicssalesUserOutput| SalesforceUserOutput; + | AttioUserOutput | MicrosoftdynamicssalesUserOutput | SalesforceUserOutput; export type CrmObjectOutput = | OriginalContactOutput diff --git a/packages/api/src/crm/company/company.module.ts b/packages/api/src/crm/company/company.module.ts index b61a8755d..2f46c8c6b 100644 --- a/packages/api/src/crm/company/company.module.ts +++ b/packages/api/src/crm/company/company.module.ts @@ -22,6 +22,8 @@ import { ZohoService } from './services/zoho'; import { ZohoCompanyMapper } from './services/zoho/mappers'; import { SyncService } from './sync/sync.service'; import { SalesforceCompanyMapper } from './services/salesforce/mappers'; +import { AffinityCompanyMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; @Module({ controllers: [CompanyController], @@ -56,4 +58,4 @@ import { SalesforceCompanyMapper } from './services/salesforce/mappers'; ], exports: [SyncService, ServiceRegistry, WebhookService], }) -export class CompanyModule {} +export class CompanyModule { } diff --git a/packages/api/src/crm/contact/contact.module.ts b/packages/api/src/crm/contact/contact.module.ts index 06a7119d3..4cbff87f3 100644 --- a/packages/api/src/crm/contact/contact.module.ts +++ b/packages/api/src/crm/contact/contact.module.ts @@ -23,6 +23,8 @@ import { ZohoService } from './services/zoho'; import { ZohoContactMapper } from './services/zoho/mappers'; import { SyncService } from './sync/sync.service'; import { SalesforceContactMapper } from './services/salesforce/mappers'; +import { AffinityContactMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; @Module({ controllers: [ContactController], @@ -57,4 +59,4 @@ import { SalesforceContactMapper } from './services/salesforce/mappers'; ], exports: [SyncService, ServiceRegistry, WebhookService], }) -export class ContactModule {} +export class ContactModule { } diff --git a/packages/api/src/crm/deal/deal.module.ts b/packages/api/src/crm/deal/deal.module.ts index 9ee57ddff..7605c4f81 100644 --- a/packages/api/src/crm/deal/deal.module.ts +++ b/packages/api/src/crm/deal/deal.module.ts @@ -22,6 +22,8 @@ import { ZohoService } from './services/zoho'; import { ZohoDealMapper } from './services/zoho/mappers'; import { SyncService } from './sync/sync.service'; import { SalesforceDealMapper } from './services/salesforce/mappers'; +import { AffinityDealMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; @Module({ controllers: [DealController], @@ -55,4 +57,4 @@ import { SalesforceDealMapper } from './services/salesforce/mappers'; ], exports: [SyncService, ServiceRegistry, WebhookService], }) -export class DealModule {} +export class DealModule { } diff --git a/packages/api/src/crm/note/note.module.ts b/packages/api/src/crm/note/note.module.ts index 87ccc0eaf..a06e842bc 100644 --- a/packages/api/src/crm/note/note.module.ts +++ b/packages/api/src/crm/note/note.module.ts @@ -22,6 +22,8 @@ import { ZohoService } from './services/zoho'; import { ZohoNoteMapper } from './services/zoho/mappers'; import { SyncService } from './sync/sync.service'; import { SalesforceNoteMapper } from './services/salesforce/mappers'; +import { AffinityNoteMapper } from './services/affinity/mappers'; +import { AffinityService } from './services/affinity'; @Module({ controllers: [NoteController], providers: [ @@ -54,4 +56,4 @@ import { SalesforceNoteMapper } from './services/salesforce/mappers'; ], exports: [SyncService, ServiceRegistry, WebhookService], }) -export class NoteModule {} +export class NoteModule { } diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index b15cc0453..0f96aed78 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -6,6 +6,7 @@ export enum CrmConnectors { ATTIO = 'attio', CLOSE = 'close', MICROSOFTDYNAMICSSALES = 'microsoftdynamicssales', + AFFINITY = 'affinity', } export enum EcommerceConnectors {