From 6fbda50bc03fa30b38c561a93b59a8f9f32f3a6b Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Mon, 22 May 2017 16:40:18 +0530 Subject: [PATCH 1/9] Project Status, Ref Code, Cancel Reason now saved to SFDC whenever a project is updated in Connect --- consumer/README.md | 15 +++++- consumer/src/scheduled-worker.js | 4 +- consumer/src/services/ConsumerService.js | 55 ++++++++++++++++++---- consumer/src/services/SalesforceService.js | 28 +++++++++++ consumer/src/worker.js | 4 +- consumer/test/ConsumerService.spec.js | 36 ++++++++++---- 6 files changed, 119 insertions(+), 23 deletions(-) diff --git a/consumer/README.md b/consumer/README.md index 7bacafe..a6b128c 100644 --- a/consumer/README.md +++ b/consumer/README.md @@ -176,6 +176,13 @@ Use following JSON for testing ``` { "id": 1, + "status": "draft", + "details": { + "utm": { + "code": "123" + } + }, + "cancelReason":null, "members": [ { "userId": 40135978, @@ -195,7 +202,13 @@ Use following JSON for testing }, "updated": { "id": 1, - "status": "active" + "status": "active", + "cancelReason": "Spam", + "details": { + "utm": { + "code": "123" + } + } } } ``` diff --git a/consumer/src/scheduled-worker.js b/consumer/src/scheduled-worker.js index b82d6eb..5f70e72 100644 --- a/consumer/src/scheduled-worker.js +++ b/consumer/src/scheduled-worker.js @@ -25,8 +25,8 @@ process.once('SIGINT', () => { }); let EVENT_HANDLERS = { - [EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED]: ConsumerService.processProjectCreated - // [EVENT.ROUTING_KEY.PROJECT_UPDATED]: ConsumerService.processProjectUpdated + [EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED]: ConsumerService.processProjectCreated, + [EVENT.ROUTING_KEY.PROJECT_UPDATED]: ConsumerService.processProjectUpdated } function close() { diff --git a/consumer/src/services/ConsumerService.js b/consumer/src/services/ConsumerService.js index 717c451..9748305 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -34,6 +34,27 @@ const projectUpdatedSchema = Joi.object().keys({ }).required() }).unknown(true); +function getUpdatedLeadFieldData(projectUpdated) { + const updatedLead = {}; + + if (projectUpdated.status) { + updatedLead.TC_Connect_Project_Status__c = projectUpdated.status; + } + + if (projectUpdated.cancelReason) { + updatedLead.TC_Connect_Cancel_Reason__c = projectUpdated.cancelReason; + } + + if (projectUpdated.details && projectUpdated.details.utm) { + if (projectUpdated.details.utm.code) { + updatedLead.TC_Connect_Ref_Code__c = projectUpdated.details.utm.code; + } + } + + return updatedLead; +} + + class ConsumerService { /** @@ -65,6 +86,10 @@ class ConsumerService { Company: company, OwnerId: config.ownerId, TC_Connect_Project_Id__c: project.id, + TC_Connect_Project_Status__c: _.get(project,"status",""), + TC_Connect_Ref_Code__c: _.get(project, "details.utm.code",""), + TC_Connect_Cancel_Reason__c: _.get(project,"cancelReason","") + }; return SalesforceService.createObject('Lead', lead, accessToken, instanceUrl) .then((leadId) => { @@ -84,6 +109,7 @@ class ConsumerService { }); } + /** * Handle created/launched project * @param {Object} projectEvent the project @@ -92,6 +118,10 @@ class ConsumerService { processProjectUpdated(logger, projectEvent) { logger.debug(projectEvent) var project = projectEvent.original; + var projectUpdated = projectEvent.updated; + + var that = this; + return Promise.all([ ConfigurationService.getSalesforceCampaignId(), SalesforceService.authenticate(), @@ -106,15 +136,22 @@ class ConsumerService { if (!lead) { throw new UnprocessableError(`Cannot find Lead with TC_Connect_Project_Id__c = '${project.id}'`); } - sql = `SELECT id FROM CampaignMember WHERE LeadId = '${lead.Id}' AND CampaignId ='${campaignId}'`; - return SalesforceService.query(sql, accessToken, instanceUrl) - .then((response) => { - const {records: [member]} = response; - if (!member) { - throw new UnprocessableError(`Cannot find CampaignMember for Lead.TC_Connect_Project_Id__c = '${project.id}'`); - } - return SalesforceService.deleteObject('CampaignMember', member.Id, accessToken, instanceUrl); - }) + + const leadUpdate = getUpdatedLeadFieldData(projectUpdated); + + if (!_.isEmpty(leadUpdate)) { + return SalesforceService.updateObject(lead.Id, 'Lead', leadUpdate, accessToken, instanceUrl); + } + + // sql = `SELECT id FROM CampaignMember WHERE LeadId = '${lead.Id}' AND CampaignId ='${campaignId}'`; + // return SalesforceService.query(sql, accessToken, instanceUrl) + // .then((response) => { + // const {records: [member]} = response; + // if (!member) { + // throw new UnprocessableError(`Cannot find CampaignMember for Lead.TC_Connect_Project_Id__c = '${project.id}'`); + // } + // return SalesforceService.deleteObject('CampaignMember', member.Id, accessToken, instanceUrl); + // }) }) }); } diff --git a/consumer/src/services/SalesforceService.js b/consumer/src/services/SalesforceService.js index ed1bb90..17e6f5c 100644 --- a/consumer/src/services/SalesforceService.js +++ b/consumer/src/services/SalesforceService.js @@ -24,6 +24,14 @@ const createObjectSchema = { instanceUrl: Joi.string().required(), }; +const updateObjectSchema = { + id: Joi.string().required(), + type: Joi.string().required(), + params: Joi.object().required(), + accessToken: Joi.string().required(), + instanceUrl: Joi.string().required(), +}; + const deleteObjectSchema = { type: Joi.string().required(), id: Joi.string().required(), @@ -86,6 +94,26 @@ class SalesforceService { .then((res) => res.body.id); } + /** + * Update an existing object + * @param {String} type the type name + * @param {String} params the object properties + * @param {String} accessToken the access token + * @param {String} instanceUrl the salesforce instance url + * @returns {String} the updated object id + */ + @logAndValidate(['id','type', 'params', 'accessToken', 'instanceUrl'], updateObjectSchema) + updateObject(id, type, params, accessToken, instanceUrl) { + return request + .patch(`${instanceUrl}/services/data/v37.0/sobjects/${type}/${id}`) + .set({ + authorization: `Bearer ${accessToken}`, + }) + .send(params) + .end(); + } + + /** * Run the query statement * @param {String} sql the Saleforce sql statement diff --git a/consumer/src/worker.js b/consumer/src/worker.js index 85666ab..7fc835a 100644 --- a/consumer/src/worker.js +++ b/consumer/src/worker.js @@ -23,8 +23,8 @@ process.once('SIGINT', () => { }); let EVENT_HANDLERS = { - [EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED]: ConsumerService.processProjectCreated - // [EVENT.ROUTING_KEY.PROJECT_UPDATED]: ConsumerService.processProjectUpdated + [EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED]: ConsumerService.processProjectCreated, + [EVENT.ROUTING_KEY.PROJECT_UPDATED]: ConsumerService.processProjectUpdated } export function initHandlers(handlers) { diff --git a/consumer/test/ConsumerService.spec.js b/consumer/test/ConsumerService.spec.js index db51476..5ca88eb 100644 --- a/consumer/test/ConsumerService.spec.js +++ b/consumer/test/ConsumerService.spec.js @@ -24,8 +24,15 @@ describe('ConsumerService', () => { instanceUrl: 'http://fake-domain', }; const userId = 40135978; + const project = { id: 1, + details: { + utm: { + code: "123" + } + }, + cancelReason: null, members: [ { id: 1234, @@ -42,7 +49,8 @@ describe('ConsumerService', () => { }, updated: { id: 1, - status: 'active' + status: 'active', + cancelReason: null } } let sandbox; @@ -64,6 +72,7 @@ describe('ConsumerService', () => { describe('processProjectCreated', () => { it('should process project successfully', async() => { + const expectedLead = { FirstName: 'john', LastName: 'doe', @@ -72,6 +81,9 @@ describe('ConsumerService', () => { Company: 'Unknown', OwnerId: config.ownerId, TC_Connect_Project_Id__c: 1, + TC_Connect_Ref_Code__c: '123', + TC_Connect_Project_Status__c: '', + TC_Connect_Cancel_Reason__c: null }; const expectedCampaignMember = { @@ -133,20 +145,25 @@ describe('ConsumerService', () => { it('should process project successfully', async() => { const memberId = 'member-id'; const leadSql = `SELECT id FROM Lead WHERE TC_Connect_Project_Id__c = '${project.id}'`; - const memberSql = `SELECT id FROM CampaignMember WHERE LeadId = '${leadId}' AND CampaignId ='${sfCampaignId}'`; + // const memberSql = `SELECT id FROM CampaignMember WHERE LeadId = '${leadId}' AND CampaignId ='${sfCampaignId}'`; const queryStub = sandbox.stub(SalesforceService, 'query'); + + queryStub.onCall(0) .returns(Promise.resolve({ records: [{ Id: leadId }] })); - queryStub.onCall(1) - .returns(Promise.resolve({ records: [{ Id: memberId }] })); - const deleteObjectStub = sandbox.stub(SalesforceService, 'deleteObject'); + // queryStub.onCall(1) + // .returns(Promise.resolve({ records: [{ Id: memberId }] })); + // const deleteObjectStub = sandbox.stub(SalesforceService, 'deleteObject'); + + const updateStub = sandbox.stub(SalesforceService,'updateObject', async() => {}); + await ConsumerService.processProjectUpdated(logger, projectUpdatePaylod); queryStub.should.have.been.calledWith(leadSql, sfAuth.accessToken, sfAuth.instanceUrl); - queryStub.should.have.been.calledWith(memberSql, sfAuth.accessToken, sfAuth.instanceUrl); - deleteObjectStub.should.have.been.calledWith('CampaignMember', memberId, sfAuth.accessToken, - sfAuth.instanceUrl); + // queryStub.should.have.been.calledWith(memberSql, sfAuth.accessToken, sfAuth.instanceUrl); + // deleteObjectStub.should.have.been.calledWith('CampaignMember', memberId, sfAuth.accessToken, + // sfAuth.instanceUrl); }); it('should throw UnprocessableError if Lead cannot be found', async() => { @@ -158,7 +175,8 @@ describe('ConsumerService', () => { queryStub.should.have.been.called; }); - it('should throw UnprocessableError if CampaignMember cannot be found', async() => { + // Not a valid use case any more + xit('should throw UnprocessableError if CampaignMember cannot be found', async() => { const queryStub = sandbox.stub(SalesforceService, 'query'); queryStub.onCall(0) .returns(Promise.resolve({ records: [{ Id: leadId }] })); From 3d3f68e5d0a370c7de5d3534ed8f0e362ed990a1 Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Mon, 22 May 2017 16:59:57 +0530 Subject: [PATCH 2/9] Update circle.yml Added feature branch in circle.yml to build --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 80e13b2..9eb3dfc 100644 --- a/circle.yml +++ b/circle.yml @@ -15,10 +15,10 @@ test: deployment: development: - branch: ["dev", "circleci", "test"] + branch: ["feature/sfdc-lead-update-phase2","dev", "circleci", "test"] commands: - ./deploy/eb-deploy.sh tc-connect2sf DEV $CIRCLE_BUILD_NUM production: branch: master commands: - - ./deploy/eb-deploy.sh tc-connect2sf PROD $CIRCLE_BUILD_NUM \ No newline at end of file + - ./deploy/eb-deploy.sh tc-connect2sf PROD $CIRCLE_BUILD_NUM From 4cb6fac5f1a8e9b7d0b55a1c80e47dabd4713c2c Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 22 May 2017 17:04:07 +0530 Subject: [PATCH 3/9] dummy commit to trigger build --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 9eb3dfc..d4d8be2 100644 --- a/circle.yml +++ b/circle.yml @@ -15,7 +15,7 @@ test: deployment: development: - branch: ["feature/sfdc-lead-update-phase2","dev", "circleci", "test"] + branch: ["dev", "circleci", "test", "feature/sfdc-lead-update-phase2"] commands: - ./deploy/eb-deploy.sh tc-connect2sf DEV $CIRCLE_BUILD_NUM production: From c527d3491ab2e578e076bcd05b0b16cde7f48ce4 Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Mon, 22 May 2017 17:36:23 +0530 Subject: [PATCH 4/9] Added Check for Blank utm code --- consumer/src/services/ConsumerService.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/consumer/src/services/ConsumerService.js b/consumer/src/services/ConsumerService.js index 9748305..ccbb7a1 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -45,10 +45,8 @@ function getUpdatedLeadFieldData(projectUpdated) { updatedLead.TC_Connect_Cancel_Reason__c = projectUpdated.cancelReason; } - if (projectUpdated.details && projectUpdated.details.utm) { - if (projectUpdated.details.utm.code) { - updatedLead.TC_Connect_Ref_Code__c = projectUpdated.details.utm.code; - } + if (projectUpdated.details) { + updatedLead.TC_Connect_Ref_Code__c = _.get(projectUpdated,"details.utm.code", ""); } return updatedLead; From 18920f5a073593fac7774c958afeff17083b0086 Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Mon, 22 May 2017 17:38:39 +0530 Subject: [PATCH 5/9] Dummy Commit to initiate build --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index d4d8be2..7ace2dd 100644 --- a/circle.yml +++ b/circle.yml @@ -15,7 +15,7 @@ test: deployment: development: - branch: ["dev", "circleci", "test", "feature/sfdc-lead-update-phase2"] + branch: ["feature/sfdc-lead-update-phase2", "dev", "circleci", "test"] commands: - ./deploy/eb-deploy.sh tc-connect2sf DEV $CIRCLE_BUILD_NUM production: From a37a4b24fdb6a10aacd13794ac7c7a9dd7516ead Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Mon, 22 May 2017 17:48:00 +0530 Subject: [PATCH 6/9] Removed feature Branch from auto build --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 7ace2dd..763373e 100644 --- a/circle.yml +++ b/circle.yml @@ -15,7 +15,7 @@ test: deployment: development: - branch: ["feature/sfdc-lead-update-phase2", "dev", "circleci", "test"] + branch: ["dev", "circleci", "test"] commands: - ./deploy/eb-deploy.sh tc-connect2sf DEV $CIRCLE_BUILD_NUM production: From 11bfe9f42c09493be5edfb189857c29ccfde77f1 Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Tue, 23 May 2017 11:29:53 +0530 Subject: [PATCH 7/9] Added If condition to skip updating converted Leads and changed the field from Connect Ref Code to existing field ref code --- consumer/src/services/ConsumerService.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/consumer/src/services/ConsumerService.js b/consumer/src/services/ConsumerService.js index ccbb7a1..2ff1959 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -46,7 +46,7 @@ function getUpdatedLeadFieldData(projectUpdated) { } if (projectUpdated.details) { - updatedLead.TC_Connect_Ref_Code__c = _.get(projectUpdated,"details.utm.code", ""); + updatedLead.Ref_Code__c = _.get(projectUpdated,"details.utm.code", ""); } return updatedLead; @@ -85,9 +85,8 @@ class ConsumerService { OwnerId: config.ownerId, TC_Connect_Project_Id__c: project.id, TC_Connect_Project_Status__c: _.get(project,"status",""), - TC_Connect_Ref_Code__c: _.get(project, "details.utm.code",""), + Ref_Code__c: _.get(project, "details.utm.code",""), TC_Connect_Cancel_Reason__c: _.get(project,"cancelReason","") - }; return SalesforceService.createObject('Lead', lead, accessToken, instanceUrl) .then((leadId) => { @@ -118,7 +117,6 @@ class ConsumerService { var project = projectEvent.original; var projectUpdated = projectEvent.updated; - var that = this; return Promise.all([ ConfigurationService.getSalesforceCampaignId(), @@ -126,18 +124,19 @@ class ConsumerService { ]).then((responses) => { const campaignId = responses[0]; const { accessToken, instanceUrl } = responses[1]; + // queries existing lead for the project - let sql = `SELECT id FROM Lead WHERE TC_Connect_Project_Id__c = '${project.id}'`; + let sql = `SELECT id,IsConverted FROM Lead WHERE TC_Connect_Project_Id__c = '${project.id}'`; return SalesforceService.query(sql, accessToken, instanceUrl) .then((response) => { const {records: [lead]} = response; if (!lead) { - throw new UnprocessableError(`Cannot find Lead with TC_Connect_Project_Id__c = '${project.id}'`); + throw new UnprocessableError(`Cannot find Lead with TC_Connect_Project_Id__c = '${project.id}'`); } const leadUpdate = getUpdatedLeadFieldData(projectUpdated); - if (!_.isEmpty(leadUpdate)) { + if (lead.IsConverted != true && !_.isEmpty(leadUpdate)) { return SalesforceService.updateObject(lead.Id, 'Lead', leadUpdate, accessToken, instanceUrl); } From 6c3d53e77b4675859f41b36e8978a9504f18f3e9 Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Tue, 23 May 2017 12:11:59 +0530 Subject: [PATCH 8/9] Fixed Test Methods after changing the field name --- consumer/test/ConsumerService.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consumer/test/ConsumerService.spec.js b/consumer/test/ConsumerService.spec.js index 5ca88eb..a595cc0 100644 --- a/consumer/test/ConsumerService.spec.js +++ b/consumer/test/ConsumerService.spec.js @@ -81,7 +81,7 @@ describe('ConsumerService', () => { Company: 'Unknown', OwnerId: config.ownerId, TC_Connect_Project_Id__c: 1, - TC_Connect_Ref_Code__c: '123', + Ref_Code__c: '123', TC_Connect_Project_Status__c: '', TC_Connect_Cancel_Reason__c: null }; @@ -144,7 +144,7 @@ describe('ConsumerService', () => { describe('processProjectUpdated', () => { it('should process project successfully', async() => { const memberId = 'member-id'; - const leadSql = `SELECT id FROM Lead WHERE TC_Connect_Project_Id__c = '${project.id}'`; + const leadSql = `SELECT id,IsConverted FROM Lead WHERE TC_Connect_Project_Id__c = '${project.id}'`; // const memberSql = `SELECT id FROM CampaignMember WHERE LeadId = '${leadId}' AND CampaignId ='${sfCampaignId}'`; const queryStub = sandbox.stub(SalesforceService, 'query'); From f93841f2405284e191f3c1babedfbd12c9fbecff Mon Sep 17 00:00:00 2001 From: Ritesh Gupta Date: Thu, 25 May 2017 11:20:46 +0530 Subject: [PATCH 9/9] Added Direct Project Id as a part of Connect Lead Sync To SFDC --- consumer/README.md | 2 ++ consumer/src/services/ConsumerService.js | 4 ++++ consumer/test/ConsumerService.spec.js | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/consumer/README.md b/consumer/README.md index a6b128c..214edd3 100644 --- a/consumer/README.md +++ b/consumer/README.md @@ -182,6 +182,7 @@ Use following JSON for testing "code": "123" } }, + "directProjectId": 5001, "cancelReason":null, "members": [ { @@ -203,6 +204,7 @@ Use following JSON for testing "updated": { "id": 1, "status": "active", + "directProjectId": 6001, "cancelReason": "Spam", "details": { "utm": { diff --git a/consumer/src/services/ConsumerService.js b/consumer/src/services/ConsumerService.js index 2ff1959..8371ccd 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -48,6 +48,9 @@ function getUpdatedLeadFieldData(projectUpdated) { if (projectUpdated.details) { updatedLead.Ref_Code__c = _.get(projectUpdated,"details.utm.code", ""); } + if (projectUpdated.directProjectId) { + updatedLead.TC_Connect_Direct_Project_Id__c = _.get(projectUpdated, "directProjectId",""); + } return updatedLead; } @@ -86,6 +89,7 @@ class ConsumerService { TC_Connect_Project_Id__c: project.id, TC_Connect_Project_Status__c: _.get(project,"status",""), Ref_Code__c: _.get(project, "details.utm.code",""), + TC_Connect_Direct_Project_Id__c: _.get(project, "directProjectId",""), TC_Connect_Cancel_Reason__c: _.get(project,"cancelReason","") }; return SalesforceService.createObject('Lead', lead, accessToken, instanceUrl) diff --git a/consumer/test/ConsumerService.spec.js b/consumer/test/ConsumerService.spec.js index a595cc0..195c7e8 100644 --- a/consumer/test/ConsumerService.spec.js +++ b/consumer/test/ConsumerService.spec.js @@ -83,7 +83,8 @@ describe('ConsumerService', () => { TC_Connect_Project_Id__c: 1, Ref_Code__c: '123', TC_Connect_Project_Status__c: '', - TC_Connect_Cancel_Reason__c: null + TC_Connect_Cancel_Reason__c: null, + TC_Connect_Direct_Project_Id__c: '' }; const expectedCampaignMember = {