diff --git a/circle.yml b/circle.yml index 80e13b2..763373e 100644 --- a/circle.yml +++ b/circle.yml @@ -21,4 +21,4 @@ deployment: 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 diff --git a/consumer/README.md b/consumer/README.md index 7bacafe..214edd3 100644 --- a/consumer/README.md +++ b/consumer/README.md @@ -176,6 +176,14 @@ Use following JSON for testing ``` { "id": 1, + "status": "draft", + "details": { + "utm": { + "code": "123" + } + }, + "directProjectId": 5001, + "cancelReason":null, "members": [ { "userId": 40135978, @@ -195,7 +203,14 @@ Use following JSON for testing }, "updated": { "id": 1, - "status": "active" + "status": "active", + "directProjectId": 6001, + "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..8371ccd 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -34,6 +34,28 @@ 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) { + updatedLead.Ref_Code__c = _.get(projectUpdated,"details.utm.code", ""); + } + if (projectUpdated.directProjectId) { + updatedLead.TC_Connect_Direct_Project_Id__c = _.get(projectUpdated, "directProjectId",""); + } + + return updatedLead; +} + + class ConsumerService { /** @@ -65,6 +87,10 @@ class ConsumerService { Company: company, OwnerId: config.ownerId, 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) .then((leadId) => { @@ -84,6 +110,7 @@ class ConsumerService { }); } + /** * Handle created/launched project * @param {Object} projectEvent the project @@ -92,29 +119,40 @@ class ConsumerService { processProjectUpdated(logger, projectEvent) { logger.debug(projectEvent) var project = projectEvent.original; + var projectUpdated = projectEvent.updated; + + return Promise.all([ ConfigurationService.getSalesforceCampaignId(), SalesforceService.authenticate(), ]).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 (lead.IsConverted != true && !_.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); - }) + + // 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..195c7e8 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,10 @@ describe('ConsumerService', () => { Company: 'Unknown', OwnerId: config.ownerId, TC_Connect_Project_Id__c: 1, + Ref_Code__c: '123', + TC_Connect_Project_Status__c: '', + TC_Connect_Cancel_Reason__c: null, + TC_Connect_Direct_Project_Id__c: '' }; const expectedCampaignMember = { @@ -132,21 +145,26 @@ 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 memberSql = `SELECT id FROM CampaignMember WHERE LeadId = '${leadId}' AND CampaignId ='${sfCampaignId}'`; + 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'); + + 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 +176,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 }] }));