From fce3e9f1cedb6d8c716e651b70fbb633d098131b Mon Sep 17 00:00:00 2001 From: Emile Bex Date: Mon, 30 Oct 2023 12:54:51 +0100 Subject: [PATCH 1/2] fix(sf): try fixing locked row error --- .../salesforce/salesforce.service.ts | 101 ++++++++++++------ .../salesforce/salesforce.types.ts | 2 + tests/mocks.types.ts | 1 + 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/external-services/salesforce/salesforce.service.ts b/src/external-services/salesforce/salesforce.service.ts index 70dde70e..471de77e 100644 --- a/src/external-services/salesforce/salesforce.service.ts +++ b/src/external-services/salesforce/salesforce.service.ts @@ -396,21 +396,42 @@ export class SalesforceService { } async createOrUpdateTask(params: TaskProps | TaskProps[]) { - let records: SalesforceTask | SalesforceTask[]; if (Array.isArray(params)) { - records = params.map((singleParams) => { + const records = params.map((singleParams) => { return mapSalesforceTaskFields(singleParams); }); + + return this.upsertRecord( + ObjectNames.TASK, + records, + 'ID_Externe__c', + 'findTaskById' + ); } else { - records = mapSalesforceTaskFields(params); + const record = mapSalesforceTaskFields(params); + try { + return (await this.upsertRecord( + ObjectNames.TASK, + record, + 'ID_Externe__c', + 'findTaskById' + )) as string; + } catch (err) { + if ( + (err as SalesforceError).errorCode === + ErrorCodes.CANNOT_UPDATE_CONVERTED_LEAD + ) { + return (await this.upsertRecord( + ObjectNames.TASK, + { ...record, WhoId: params.contactSfId }, + 'ID_Externe__c', + 'findTaskById' + )) as string; + } + console.error(err); + throw err; + } } - - return this.upsertRecord( - ObjectNames.TASK, - records, - 'ID_Externe__c', - 'findTaskById' - ); } async searchAccountByName(search: string, recordType: AccountRecordType) { @@ -983,10 +1004,10 @@ export class SalesforceService { zone, autreSource: 'Formulaire_Sourcing_Page_Travailler', } as const; - const leadId = (await this.createCandidateLead(leadToCreate)) as string; if (infoCo) { try { + const leadId = (await this.createCandidateLead(leadToCreate)) as string; await this.createCampaignMemberInfoCo({ leadId }, infoCo); return leadId; } catch (err) { @@ -996,12 +1017,12 @@ export class SalesforceService { (err as SalesforceError).errorCode === ErrorCodes.FIELD_INTEGRITY_EXCEPTION ) { - const contactId = await this.findContact(email); + const contactSfId = await this.findContact(email); await this.createCampaignMemberInfoCo( - { contactId: contactId }, + { contactId: contactSfId }, infoCo ); - return leadId; + return contactSfId; } console.error(err); throw err; @@ -1009,35 +1030,48 @@ export class SalesforceService { } } - async createCampaignMemberInfoCo( + async findOrCreateCampaignMember( leadOrContactId: { leadId?: string; contactId?: string }, infoCoId: string ) { try { - let campaignMemberId = await this.findCampaignMember( + const campaignMemberSfId = await this.findCampaignMember( leadOrContactId, infoCoId ); const { leadId, contactId } = leadOrContactId; - if (!campaignMemberId) { - campaignMemberId = (await this.createRecord( - ObjectNames.CAMPAIGN_MEMBER, - { - ...(leadId - ? { - LeadId: leadId, - } - : { ContactId: contactId }), - CampaignId: infoCoId, - Status: 'Inscrit', - } - )) as string; + if (!campaignMemberSfId) { + await this.createRecord(ObjectNames.CAMPAIGN_MEMBER, { + ...(leadId + ? { + LeadId: leadId, + } + : { ContactId: contactId }), + CampaignId: infoCoId, + Status: 'Inscrit', + }); } - return campaignMemberId; } catch (err) { - // Case where the flow is not as fast as the api call if ((err as SalesforceError).errorCode === ErrorCodes.DUPLICATE_VALUE) { - return this.findCampaignMember(leadOrContactId, infoCoId); + return; + } + console.error(err); + throw err; + } + } + + async createCampaignMemberInfoCo( + leadOrContactId: { leadId?: string; contactId?: string }, + infoCoId: string + ) { + try { + await this.findOrCreateCampaignMember(leadOrContactId, infoCoId); + } catch (err) { + if ( + (err as SalesforceError).errorCode !== ErrorCodes.UNABLE_TO_LOCK_ROW + ) { + await asyncTimeout(1000); + await this.findOrCreateCampaignMember(leadOrContactId, infoCoId); } console.error(err); throw err; @@ -1281,11 +1315,14 @@ export class SalesforceService { const ownerSfId = await this.findOwnerByLeadSfId(leadSfId); + const contactSfId = await this.findContact(email); + return { binomeSfId, externalMessageId, ownerSfId, leadSfId, + contactSfId, subject: `Message envoyé à ${candidateFirstName} ${candidateLastName} LinkedOut via le site`, zone, }; diff --git a/src/external-services/salesforce/salesforce.types.ts b/src/external-services/salesforce/salesforce.types.ts index 6e6467f3..644ca386 100644 --- a/src/external-services/salesforce/salesforce.types.ts +++ b/src/external-services/salesforce/salesforce.types.ts @@ -44,6 +44,7 @@ export const ErrorCodes = { FIELD_INTEGRITY_EXCEPTION: 'FIELD_INTEGRITY_EXCEPTION', FIELD_FILTER_VALIDATION_EXCEPTION: 'FIELD_FILTER_VALIDATION_EXCEPTION', NOT_FOUND: 'NOT_FOUND', + UNABLE_TO_LOCK_ROW: 'UNABLE_TO_LOCK_ROW', } as const; export type ErrorCode = typeof ErrorCodes[keyof typeof ErrorCodes]; @@ -423,6 +424,7 @@ export interface TaskProps { subject: string; ownerSfId: string; leadSfId: string; + contactSfId?: string; binomeSfId: string; zone: CompanyZone; } diff --git a/tests/mocks.types.ts b/tests/mocks.types.ts index 568f610c..dd0ce9b9 100644 --- a/tests/mocks.types.ts +++ b/tests/mocks.types.ts @@ -93,6 +93,7 @@ export const SalesforceMocks: ProviderMock = { setIsWorker: jest.fn(), updateLead: jest.fn(), findCampaignMember: jest.fn(), + findOrCreateCampaignMember: jest.fn(), findOrCreateLeadFromInscriptionCandidateForm: jest.fn(), findOrCreateLeadFromExternalMessage: jest.fn(), createCampaignMemberInfoCo: jest.fn(), From 2998b86493f4f7e56ec42a3cc9a5de5f215dd9f4 Mon Sep 17 00:00:00 2001 From: Emile Bex Date: Mon, 30 Oct 2023 12:56:14 +0100 Subject: [PATCH 2/2] v2.15.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 867b43f8..d319366b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linkedout-backend", - "version": "2.15.4", + "version": "2.15.5", "license": "ISC", "engines": { "node": "16.x"