From 0e1dfea98d776dc0540886914573846701ed0ae6 Mon Sep 17 00:00:00 2001 From: vikasrohit Date: Mon, 6 Jan 2020 17:18:03 +0530 Subject: [PATCH 1/2] Hotfix/skip user validation for project created event (#48) * Removed customer user validation for generating the project created event * Passed additional first name and last name along with project events as well --- consumer/src/services/ConsumerService.js | 15 +++---- consumer/test/ConsumerService.spec.js | 52 ++++++++++++++++-------- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/consumer/src/services/ConsumerService.js b/consumer/src/services/ConsumerService.js index c7470e7..49d0793 100644 --- a/consumer/src/services/ConsumerService.js +++ b/consumer/src/services/ConsumerService.js @@ -9,7 +9,6 @@ import IdentityService from './IdentityService'; import SalesforceService from './SalesforceService'; import {UnprocessableError} from '../common/errors'; -const memberRole = 'customer'; const duplicateRecordRegex = /TC_Connect_Project_Id__c duplicates value on record/; const projectCreatedSchema = Joi.object().keys({ @@ -40,18 +39,15 @@ class ConsumerService { @log(['project']) @validate(['logger','project'], projectCreatedSchema) processProjectCreated(logger, project) { - const member = _.find(project.members, {role: memberRole, isPrimary: true}); - if (!member) { - logger.info('Project Members:'); - logger.info(project.members); - throw new UnprocessableError('Cannot find primary customer'); - } + logger.info(`Project Created By: ${project.createdBy}`); return Promise.all([ - IdentityService.getUser(member.userId), + IdentityService.getUser(project.createdBy), SalesforceService.authenticate(), ]).then((responses) => { const user = responses[0]; project.createdByEmail = user.email; + project.createdByFirstName = user.firstName; + project.createdByLastName = user.lastName; const { accessToken, instanceUrl } = responses[1]; const leadData = { Type__c: 'connect.project.created', @@ -80,7 +76,6 @@ class ConsumerService { @log(['projectEvent']) @validate(['logger', 'projectEvent'], projectUpdatedSchema) processProjectUpdated(logger, projectEvent) { - logger.debug(projectEvent) delete projectEvent.original.template; delete projectEvent.updated.template; var project = projectEvent.original; @@ -93,6 +88,8 @@ class ConsumerService { const user = responses[0]; const { accessToken, instanceUrl } = responses[1]; projectEvent.original.createdByEmail = user.email; + projectEvent.original.createdByFirstName = user.firstName; + projectEvent.original.createdByLastName = user.lastName; const leadData = { Type__c: 'connect.project.updated', diff --git a/consumer/test/ConsumerService.spec.js b/consumer/test/ConsumerService.spec.js index 63ebb4d..c6e4ce3 100644 --- a/consumer/test/ConsumerService.spec.js +++ b/consumer/test/ConsumerService.spec.js @@ -51,13 +51,12 @@ describe('ConsumerService', () => { isPrimary: true, }, ], - createdByEmail : "jd@example.com" + createdBy: userId }; const projectUpdatePaylod = { original: { id: 1, - status: 'in_review', - createdByEmail : "jd@example.com" + status: 'in_review' }, updated: { id: 1, @@ -84,7 +83,12 @@ describe('ConsumerService', () => { it('should process project successfully', async() => { const expectedLead = { Type__c: 'connect.project.created', - Json__c: JSON.stringify(project) + Json__c: JSON.stringify({ + ...project, + createdByEmail : user.email, + createdByFirstName: user.firstName, + createdByLastName: user.lastName + }) }; const createObjectStub = sandbox.stub(SalesforceService, 'createObject', async() => leadId); @@ -95,19 +99,27 @@ describe('ConsumerService', () => { createObjectStub.should.have.been.calledWith('Connect_Event__c', expectedLead, sfAuth.accessToken, sfAuth.instanceUrl); }); - it('should throw UnprocessableError primary customer is not found', async() => { + it('should NOT throw any error even if primary customer is not found', async() => { const projectWihoutMembers = { - id: 1, - members: [], + ...project, + members : [] }; - try { - ConsumerService.processProjectCreated(logger, projectWihoutMembers); - sinon.fail('Should be rejected'); - } catch(err) { - expect(err).to.exist - .and.be.instanceof(UnprocessableError) - .and.have.property('message').and.match(/Cannot find primary customer/); - } + const expectedLead = { + Type__c: 'connect.project.created', + Json__c: JSON.stringify({ + ...projectWihoutMembers, + createdByEmail : user.email, + createdByFirstName: user.firstName, + createdByLastName: user.lastName + }) + }; + + const createObjectStub = sandbox.stub(SalesforceService, 'createObject', async() => leadId); + + await ConsumerService.processProjectCreated(logger, projectWihoutMembers); + getUserStub.should.have.been.calledWith(userId); + authenticateStub.should.have.been.called; + createObjectStub.should.have.been.calledWith('Connect_Event__c', expectedLead, sfAuth.accessToken, sfAuth.instanceUrl); }); it('should rethrow Error from createObject if error is not duplicate', async() => { @@ -128,7 +140,15 @@ describe('ConsumerService', () => { const memberId = 'member-id'; const expectedLead = { Type__c: 'connect.project.updated', - Json__c: JSON.stringify(projectUpdatePaylod) + Json__c: JSON.stringify({ + original: { + ...projectUpdatePaylod.original, + createdByEmail : user.email, + createdByFirstName: user.firstName, + createdByLastName: user.lastName + }, + updated: projectUpdatePaylod.updated + }) }; const createObjectStub = sandbox.stub(SalesforceService,'createObject', async() => {}); From fa8f466150eabbd0421569bddc3d6beb7a30bddb Mon Sep 17 00:00:00 2001 From: vikasrohit Date: Thu, 27 Feb 2020 11:42:57 +0530 Subject: [PATCH 2/2] Feature/opty lead status sync with project (#50) * Reading new SFDC events to act appropriately * Fixed case sensitivity of connect project id field * V5 request format for projects api call * Handling cancel reason for the cases where we have to cancel the project * Handled the lead.qualified logic similar to opty.create * Fixed the field to pick the correct disqualify reason for the lead --- consumer/src/salesforce-worker.js | 38 ++++++++++++++++----- consumer/src/services/ProjectService.js | 44 ++++++++++++------------- consumer/test/ProjectService.spec.js | 32 +++++++----------- consumer/test/salesforce-worker.spec.js | 6 ++-- 4 files changed, 67 insertions(+), 53 deletions(-) diff --git a/consumer/src/salesforce-worker.js b/consumer/src/salesforce-worker.js index 2092836..447e630 100644 --- a/consumer/src/salesforce-worker.js +++ b/consumer/src/salesforce-worker.js @@ -23,20 +23,42 @@ export function consumeMessage(message) { debug('Got Connect_SFDC__e', message); const payload = _.get(message, 'payload'); const eventType = _.get(payload, 'Type__c'); + const original = JSON.parse(_.get(payload, 'Original__c')); + const updated = JSON.parse(_.get(payload, 'Updated__c')); + let statusToBe = null; + let statusChangeReason = null; if (eventType === 'billingAccount.updated') { - const original = JSON.parse(_.get(payload, 'Original__c')); - const updated = JSON.parse(_.get(payload, 'Updated__c')); const oldStatus = _.get(original, 'Active__c'); const updatedStatus = _.get(updated, 'Active__c'); debug(`${oldStatus} === ${updatedStatus}`); if (oldStatus !== updatedStatus && updatedStatus === true) { - const projectId = _.get(updated, 'TC_Connect_Project_ID__c'); - debug(`Activating project with id ${projectId}`); - // TODO retrieve project id from the payload - if (projectId) { - ProjectService.activateProject(projectId); - } + statusToBe = 'active' } + } else if (eventType === 'opportunity.won') { + // TODO + } else if (eventType === 'opportunity.lost') { + // Cancel connect project + statusToBe = 'cancelled' + statusChangeReason = _.get(updated, 'Loss_Description__c', 'Opportunity Lost'); + } else if (eventType === 'lead.disqualified') { + // Cancel the project + statusToBe = 'cancelled' + statusChangeReason = _.get(updated, 'Disqualified_Reason__c', 'Lead Disqualified'); + } else if (eventType === 'opportunity.create') { + // Move to reviewed status + statusToBe = 'reviewed' + } else if (eventType === 'lead.qualified') { + // Move to reviewed status + statusToBe = 'reviewed' + } + let projectId = _.get(updated, 'TC_Connect_Project_ID__c'); + if (!projectId) { + projectId = _.get(updated, 'TC_Connect_Project_Id__c'); + } + debug(`Status to be updated: ${statusToBe} for project with id ${projectId}`); + if (statusToBe && projectId) { + debug(`Updating status to ${statusToBe} project with id ${projectId}`); + ProjectService.updateProjectStatus(projectId, statusToBe, statusChangeReason); } } diff --git a/consumer/src/services/ProjectService.js b/consumer/src/services/ProjectService.js index b19f710..7c7efb8 100644 --- a/consumer/src/services/ProjectService.js +++ b/consumer/src/services/ProjectService.js @@ -6,6 +6,8 @@ const request = require('superagent'); const config = require('config'); const _ = require('lodash'); +const debug = require('debug')('app:project-service'); + /** * Get project details * @@ -14,11 +16,8 @@ const _ = require('lodash'); * @return {Promise} promise resolved to project details */ const getProject = (projectId) => { - console.log(`AUTH0_CLIENT_ID: ${config.AUTH0_CLIENT_ID.substring(0, 5)}`); - console.log(`AUTH0_CLIENT_SECRET: ${config.AUTH0_CLIENT_SECRET.substring(0, 5)}`); - console.log(`AUTH0_URL: ${config.AUTH0_URL}`); - console.log(`AUTH0_AUDIENCE: ${config.AUTH0_AUDIENCE}`); - console.log(`AUTH0_PROXY_SERVER_URL: ${config.AUTH0_PROXY_SERVER_URL}`); + debug(`AUTH0_CLIENT_ID: ${config.AUTH0_CLIENT_ID.substring(0, 5)}`); + debug(`AUTH0_CLIENT_SECRET: ${config.AUTH0_CLIENT_SECRET.substring(0, 5)}`); return M2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) .then((token) => ( request @@ -26,13 +25,13 @@ const getProject = (projectId) => { .set('accept', 'application/json') .set('authorization', `Bearer ${token}`) .then((res) => { - if (!_.get(res, 'body.result.success')) { + if (res.status !== 200) { throw new Error(`Failed to get project details of project id: ${projectId}`); } - const project = _.get(res, 'body.result.content'); + const project = _.get(res, 'body'); return project; }).catch((err) => { - const errorDetails = _.get(err, 'response.body.result.content.message'); + const errorDetails = _.get(err, 'response.body'); throw new Error( `Failed to get project details of project id: ${projectId}.` + (errorDetails ? ' Server response: ' + errorDetails : '') @@ -52,31 +51,32 @@ const getProject = (projectId) => { * * @return {Promise} promise resolved to the updated project */ -const activateProject = (projectId) => { - console.log(`AUTH0_CLIENT_ID: ${config.AUTH0_CLIENT_ID.substring(0, 5)}`); - console.log(`AUTH0_CLIENT_SECRET: ${config.AUTH0_CLIENT_SECRET.substring(0, 5)}`); - console.log(`AUTH0_URL: ${config.AUTH0_URL}`); - console.log(`AUTH0_AUDIENCE: ${config.AUTH0_AUDIENCE}`); - console.log(`AUTH0_PROXY_SERVER_URL: ${config.AUTH0_PROXY_SERVER_URL}`); +const updateProjectStatus = (projectId, status='active', changeReason) => { + debug(`AUTH0_CLIENT_ID: ${config.AUTH0_CLIENT_ID.substring(0, 5)}`); + debug(`AUTH0_CLIENT_SECRET: ${config.AUTH0_CLIENT_SECRET.substring(0, 5)}`); + const updatedProject = { status }; + if (changeReason) { + updatedProject.cancelReason = changeReason; + } return M2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) .then((token) => ( request .patch(`${config.projectApi.url}/projects/${projectId}`) .set('accept', 'application/json') .set('Authorization', `Bearer ${token}`) - .send({ param : { status : 'active' } }) + .send(updatedProject) .then((res) => { - if (!_.get(res, 'body.result.success')) { - throw new Error(`Failed to activate project with id: ${projectId}`); + if (res.status !== 200) { + throw new Error(`Failed to update project with id: ${projectId}`); } - const project = _.get(res, 'body.result.content'); + const project = _.get(res, 'body'); if (project) { - console.log(`Successfully activated the project with id ${projectId}`); + debug(`Successfully updated the project ${projectId} with status ${status}`); } return project; }).catch((err) => { - console.log(err); - const errorDetails = _.get(err, 'response.body.result.content.message'); + debug(err); + const errorDetails = _.get(err, 'response.body'); throw new Error( `Failed to update project with id: ${projectId}.` + (errorDetails ? ' Server response: ' + errorDetails : '') @@ -91,5 +91,5 @@ const activateProject = (projectId) => { module.exports = { getProject, - activateProject + updateProjectStatus }; \ No newline at end of file diff --git a/consumer/test/ProjectService.spec.js b/consumer/test/ProjectService.spec.js index 1f8c21b..480ad5b 100644 --- a/consumer/test/ProjectService.spec.js +++ b/consumer/test/ProjectService.spec.js @@ -31,22 +31,13 @@ const authenticateResponse = { }; const getProjectResponse = { - id: '-88f3803:1557f8485b0:-b0a', - result: { - success: true, - status: 200, - metadata: null, - content: { - id: '265522', - modifiedBy: null, - modifiedAt: '2016-06-01T16:57:47.000Z', - createdBy: null, - createdAt: '2002-02-06T18:06:40.000Z', - status: 'active', - name: 'Test Project', - }, - }, - version: 'v4', + id: '265522', + modifiedBy: null, + modifiedAt: '2016-06-01T16:57:47.000Z', + createdBy: null, + createdAt: '2002-02-06T18:06:40.000Z', + status: 'active', + name: 'Test Project', }; @@ -80,8 +71,8 @@ describe('ProjectService', () => { }) .get('/projects/1234') .reply(200, getProjectResponse); - const user = await ProjectService.getProject(1234); - expect(user).to.deep.equal(getProjectResponse.result.content); + const project = await ProjectService.getProject(1234); + expect(project).to.deep.equal(getProjectResponse); fakeHttp.done(); }); @@ -93,8 +84,9 @@ describe('ProjectService', () => { }) .patch('/projects/1234') .reply(200, getProjectResponse); - const user = await ProjectService.activateProject(1234); - expect(user).to.deep.equal(getProjectResponse.result.content); + const project = await ProjectService.updateProjectStatus(1234); + console.log(project, 'project') + expect(project).to.deep.equal(getProjectResponse); fakeHttp.done(); }); }); diff --git a/consumer/test/salesforce-worker.spec.js b/consumer/test/salesforce-worker.spec.js index 19efdb7..0e29426 100644 --- a/consumer/test/salesforce-worker.spec.js +++ b/consumer/test/salesforce-worker.spec.js @@ -14,9 +14,9 @@ describe('salesforce-worker', () => { } } describe('consumeMessage', () => { - let activateProjectSpy; + let updateProjectStatusSpy; beforeEach(() => { - activateProjectSpy = ProjectService.activateProject = sinon.spy(); + updateProjectStatusSpy = ProjectService.updateProjectStatus = sinon.spy(); }); /** @@ -29,7 +29,7 @@ describe('salesforce-worker', () => { it('should consume and active project successfully', (done) => { invokeConsume(done); - activateProjectSpy.should.have.been.calledWith(1234); + updateProjectStatusSpy.should.have.been.calledWith(1234); done(); }); });