From 5e31b5bf4c5c340dffadf9b8a1542dda5e357813 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Mar 2021 17:30:18 +0530 Subject: [PATCH 1/7] feat: git#636-Need details of billing account for the project for the purpose of invoicing --- src/constants.js | 1 + src/permissions/constants.js | 21 ++++++++++- src/permissions/index.js | 4 ++ src/routes/billingAccounts/get.js | 49 +++++++++++++++++++++++++ src/routes/billingAccounts/list.js | 2 +- src/routes/billingAccounts/list.spec.js | 2 +- src/routes/index.js | 2 + src/services/salesforceService.js | 27 +++++++++++++- 8 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 src/routes/billingAccounts/get.js diff --git a/src/constants.js b/src/constants.js index 6fe0011b..ad7ba73e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -276,6 +276,7 @@ export const M2M_SCOPES = { WRITE: 'write:projects', READ_USER_BILLING_ACCOUNTS: 'read:user-billing-accounts', WRITE_PROJECTS_BILLING_ACCOUNTS: 'write:projects-billing-accounts', + READ_PROJECT_BILLING_ACCOUNT_DETAILS: 'read:project-billing-account-details', }, PROJECT_MEMBERS: { ALL: 'all:project-members', diff --git a/src/permissions/constants.js b/src/permissions/constants.js index 41b7d3be..5e4eade2 100644 --- a/src/permissions/constants.js +++ b/src/permissions/constants.js @@ -96,7 +96,14 @@ const SCOPES_PROJECTS_WRITE = [ */ const SCOPES_PROJECTS_READ_AVL_BILLING_ACCOUNTS = [ M2M_SCOPES.CONNECT_PROJECT_ADMIN, - M2M_SCOPES.READ_USER_BILLING_ACCOUNTS, + M2M_SCOPES.PROJECTS.READ_USER_BILLING_ACCOUNTS, +]; + +/** + * M2M scopes to "read" available Billing Accounts for the project + */ +const SCOPES_PROJECTS_READ_BILLING_ACCOUNT_DETAILS = [ + M2M_SCOPES.PROJECTS.READ_PROJECT_BILLING_ACCOUNT_DETAILS, ]; /** @@ -277,6 +284,18 @@ export const PERMISSION = { // eslint-disable-line import/prefer-default-export scopes: SCOPES_PROJECTS_READ_AVL_BILLING_ACCOUNTS, }, + /* + * Project Invite + */ + READ_PROJECT_BILLING_ACCOUNT_DETAILS: { + meta: { + title: 'Read details of billing accounts - only allowed to m2m calls', + group: 'Project Billing Accounts', + description: 'Who can view the details of the Billing Account attached to the project', + }, + scopes: SCOPES_PROJECTS_READ_BILLING_ACCOUNT_DETAILS, + }, + /* * Project Member */ diff --git a/src/permissions/index.js b/src/permissions/index.js index 2e62dc7f..9e4bc1e4 100644 --- a/src/permissions/index.js +++ b/src/permissions/index.js @@ -24,6 +24,10 @@ module.exports = () => { PERMISSION.READ_AVL_PROJECT_BILLING_ACCOUNTS, ])); + Authorizer.setPolicy('projectBillingAccount.view', generalPermission([ + PERMISSION.READ_PROJECT_BILLING_ACCOUNT_DETAILS, + ])); + Authorizer.setPolicy('projectMember.create', generalPermission([ PERMISSION.CREATE_PROJECT_MEMBER_OWN, PERMISSION.CREATE_PROJECT_MEMBER_NOT_OWN, diff --git a/src/routes/billingAccounts/get.js b/src/routes/billingAccounts/get.js new file mode 100644 index 00000000..6697a1d3 --- /dev/null +++ b/src/routes/billingAccounts/get.js @@ -0,0 +1,49 @@ +import _ from 'lodash'; +import validate from 'express-validation'; +import Joi from 'joi'; +import { middleware as tcMiddleware } from 'tc-core-library-js'; +import SalesforceService from '../../services/salesforceService'; +import models from '../../models'; + +/** + * API to get project attachments. + * + */ + +const permissions = tcMiddleware.permissions; + +const schema = { + params: { + projectId: Joi.number().integer().positive().required(), + }, +}; + +module.exports = [ + validate(schema), + permissions('projectBillingAccount.view'), + async (req, res, next) => { + const projectId = _.parseInt(req.params.projectId); + try { + const project = await models.Project.findOne({ + where: { id: projectId }, + attributes: ['id', 'billingAccountId'], + raw: true, + }); + const billingAccountId = project.billingAccountId; + if (!billingAccountId) { + const err = new Error('Billing Account not found'); + err.status = 404; + throw err; + } + const { accessToken, instanceUrl } = await SalesforceService.authenticate(); + // eslint-disable-next-line + const sql = `SELECT TopCoder_Billing_Account_Id__c, Mark_Up__c from Topcoder_Billing_Account__c tba where TopCoder_Billing_Account_Id__c='${billingAccountId}'`; + req.log.debug(sql); + const billingAccount = await SalesforceService.queryBillingAccount(sql, accessToken, instanceUrl, req.log); + res.json(billingAccount); + } catch (error) { + req.log.error(error); + next(error); + } + }, +]; diff --git a/src/routes/billingAccounts/list.js b/src/routes/billingAccounts/list.js index 77166d56..06a16fbb 100644 --- a/src/routes/billingAccounts/list.js +++ b/src/routes/billingAccounts/list.js @@ -29,7 +29,7 @@ module.exports = [ const sql = `SELECT Topcoder_Billing_Account__r.id, Topcoder_Billing_Account__r.TopCoder_Billing_Account_Id__c, Topcoder_Billing_Account__r.Billing_Account_Name__c, Topcoder_Billing_Account__r.Start_Date__c, Topcoder_Billing_Account__r.End_Date__c from Topcoder_Billing_Account_Resource__c tbar where UserID__c='${userId}'`; // and Topcoder_Billing_Account__r.TC_Connect_Project_ID__c='${projectId}' req.log.debug(sql); - const billingAccounts = await SalesforceService.query(sql, accessToken, instanceUrl, req.log); + const billingAccounts = await SalesforceService.queryUserBillingAccounts(sql, accessToken, instanceUrl, req.log); res.json(billingAccounts); } catch (error) { req.log.error(error); diff --git a/src/routes/billingAccounts/list.spec.js b/src/routes/billingAccounts/list.spec.js index bd76869c..7d01f533 100644 --- a/src/routes/billingAccounts/list.spec.js +++ b/src/routes/billingAccounts/list.spec.js @@ -71,7 +71,7 @@ describe('Project Billing Accounts list', () => { accessToken: 'mock', instanceUrl: 'mock_url', })); - salesforceQuery = sinon.stub(SalesforceService, 'query', () => Promise.resolve(billingAccountsData)); + salesforceQuery = sinon.stub(SalesforceService, 'queryUserBillingAccounts', () => Promise.resolve(billingAccountsData)); done(); }); }); diff --git a/src/routes/index.js b/src/routes/index.js index 5a28ee64..6b725728 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -123,6 +123,8 @@ router.route('/v5/projects/:projectId(\\d+)/scopeChangeRequests/:requestId(\\d+) router.route('/v5/projects/:projectId(\\d+)/billingAccounts') .get(require('./billingAccounts/list')); +router.route('/v5/projects/:projectId(\\d+)/billingAccount') + .get(require('./billingAccounts/get')); router.route('/v5/projects/:projectId(\\d+)/members') .get(require('./projectMembers/list')) diff --git a/src/services/salesforceService.js b/src/services/salesforceService.js index 651f772f..efb59c6a 100644 --- a/src/services/salesforceService.js +++ b/src/services/salesforceService.js @@ -54,7 +54,7 @@ class SalesforceService { * @param {Object} logger logger to be used for logging * @returns {{totalSize: Number, done: Boolean, records: Array}} the result */ - static query(sql, accessToken, instanceUrl, logger) { + static queryUserBillingAccounts(sql, accessToken, instanceUrl, logger) { return axios({ url: `${instanceUrl}/services/data/v37.0/query?q=${sql}`, method: 'get', @@ -77,6 +77,31 @@ class SalesforceService { return billingAccounts; }); } + + /** + * Run the query statement + * @param {String} sql the Saleforce sql statement + * @param {String} accessToken the access token + * @param {String} instanceUrl the salesforce instance url + * @param {Object} logger logger to be used for logging + * @returns {{totalSize: Number, done: Boolean, records: Array}} the result + */ + static queryBillingAccount(sql, accessToken, instanceUrl, logger) { + return axios({ + url: `${instanceUrl}/services/data/v37.0/query?q=${sql}`, + method: 'get', + headers: { authorization: `Bearer ${accessToken}` }, + }).then((res) => { + if (logger) { + logger.debug(_.get(res, 'data.records', [])); + } + const billingAccounts = _.get(res, 'data.records', []).map(o => ({ + tcBillingAccountId: _.get(o, 'TopCoder_Billing_Account_Id__c'), + markup: _.get(o, 'Mark_Up__c'), + })); + return billingAccounts.length > 0 ? billingAccounts[0] : {}; + }); + } } export default SalesforceService; From fe53c70b0bbdcc44040c5bbf3e8ddcd1e8a58155 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Mar 2021 17:36:29 +0530 Subject: [PATCH 2/7] fix: git#635-Show only active Billing Accounts from the billing accounts endpoint --- src/routes/billingAccounts/list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/billingAccounts/list.js b/src/routes/billingAccounts/list.js index 06a16fbb..ee80d27a 100644 --- a/src/routes/billingAccounts/list.js +++ b/src/routes/billingAccounts/list.js @@ -26,7 +26,7 @@ module.exports = [ try { const { accessToken, instanceUrl } = await SalesforceService.authenticate(); // eslint-disable-next-line - const sql = `SELECT Topcoder_Billing_Account__r.id, Topcoder_Billing_Account__r.TopCoder_Billing_Account_Id__c, Topcoder_Billing_Account__r.Billing_Account_Name__c, Topcoder_Billing_Account__r.Start_Date__c, Topcoder_Billing_Account__r.End_Date__c from Topcoder_Billing_Account_Resource__c tbar where UserID__c='${userId}'`; + const sql = `SELECT Topcoder_Billing_Account__r.id, Topcoder_Billing_Account__r.TopCoder_Billing_Account_Id__c, Topcoder_Billing_Account__r.Billing_Account_Name__c, Topcoder_Billing_Account__r.Start_Date__c, Topcoder_Billing_Account__r.End_Date__c from Topcoder_Billing_Account_Resource__c tbar where Topcoder_Billing_Account__r.Active__c=true AND UserID__c='${userId}'`; // and Topcoder_Billing_Account__r.TC_Connect_Project_ID__c='${projectId}' req.log.debug(sql); const billingAccounts = await SalesforceService.queryUserBillingAccounts(sql, accessToken, instanceUrl, req.log); From 8245fc6a94c6db8d585c4d165320f56c6f126d91 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Mar 2021 17:42:47 +0530 Subject: [PATCH 3/7] Lint fix --- src/routes/billingAccounts/list.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/billingAccounts/list.spec.js b/src/routes/billingAccounts/list.spec.js index 7d01f533..45fcf0fd 100644 --- a/src/routes/billingAccounts/list.spec.js +++ b/src/routes/billingAccounts/list.spec.js @@ -71,6 +71,7 @@ describe('Project Billing Accounts list', () => { accessToken: 'mock', instanceUrl: 'mock_url', })); + // eslint-disable-next-line salesforceQuery = sinon.stub(SalesforceService, 'queryUserBillingAccounts', () => Promise.resolve(billingAccountsData)); done(); }); From d7a7e9206c202103d20994e693ba1f50353f1217 Mon Sep 17 00:00:00 2001 From: vikasrohit Date: Fri, 5 Mar 2021 18:01:08 +0530 Subject: [PATCH 4/7] deployable feature branch --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0be8e121..d349abac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,7 +152,7 @@ workflows: - UnitTests filters: branches: - only: ['develop', 'connect-performance-testing'] + only: ['develop', 'connect-performance-testing', 'feature/get-markup-from-billing-account'] - deployProd: context : org-global requires: @@ -167,4 +167,4 @@ workflows: - deployProd - Connect-Performance-Testing: requires: - - Hold [Performance-Testing] \ No newline at end of file + - Hold [Performance-Testing] From f13bdcce67cccfd426ae8861381a97e30464154c Mon Sep 17 00:00:00 2001 From: imcaizheng Date: Wed, 10 Mar 2021 07:50:43 +0800 Subject: [PATCH 5/7] add unit test file --- src/routes/billingAccounts/get.js | 5 + src/routes/billingAccounts/get.spec.js | 170 +++++++++++++++++++++++++ src/tests/util.js | 1 + 3 files changed, 176 insertions(+) create mode 100644 src/routes/billingAccounts/get.spec.js diff --git a/src/routes/billingAccounts/get.js b/src/routes/billingAccounts/get.js index 6697a1d3..30fca7cf 100644 --- a/src/routes/billingAccounts/get.js +++ b/src/routes/billingAccounts/get.js @@ -29,6 +29,11 @@ module.exports = [ attributes: ['id', 'billingAccountId'], raw: true, }); + if (!project) { + const err = new Error(`Project with id "${projectId}" not found`); + err.status = 404; + throw err; + } const billingAccountId = project.billingAccountId; if (!billingAccountId) { const err = new Error('Billing Account not found'); diff --git a/src/routes/billingAccounts/get.spec.js b/src/routes/billingAccounts/get.spec.js new file mode 100644 index 00000000..2171ee0d --- /dev/null +++ b/src/routes/billingAccounts/get.spec.js @@ -0,0 +1,170 @@ +/* eslint-disable no-unused-expressions */ +import chai from 'chai'; +import request from 'supertest'; +import sinon from 'sinon'; + +import models from '../../models'; +import server from '../../app'; +import testUtil from '../../tests/util'; +import SalesforceService from '../../services/salesforceService'; + +chai.should(); + +// demo data which might be returned by the `SalesforceService.query` +const billingAccountData = { + sfBillingAccountId: '123', + tcBillingAccountId: 123123, + name: 'Billing Account 1', + startDate: '2021-02-10T18:51:27Z', + endDate: '2021-03-10T18:51:27Z', +}; + +describe('Project Billing Accounts list', () => { + let project1; + let project2; + let salesforceAuthenticate; + let salesforceQuery; + + beforeEach((done) => { + testUtil.clearDb() + .then(() => testUtil.clearES()) + .then(() => models.Project.create({ + type: 'generic', + directProjectId: 1, + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + return models.ProjectMember.create({ + userId: testUtil.userIds.copilot, + projectId: project1.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then(() => models.ProjectMember.create({ + userId: testUtil.userIds.member, + projectId: project1.id, + role: 'customer', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + })); + })).then(() => models.Project.create({ + type: 'generic', + directProjectId: 1, + billingAccountId: null, // do not define billingAccountId + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project2 = p; + // create members + return models.ProjectMember.create({ + userId: testUtil.userIds.copilot, + projectId: project2.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then(() => models.ProjectMember.create({ + userId: testUtil.userIds.member, + projectId: project2.id, + role: 'customer', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + })); + })) + .then(() => { + salesforceAuthenticate = sinon.stub(SalesforceService, 'authenticate', () => Promise.resolve({ + accessToken: 'mock', + instanceUrl: 'mock_url', + })); + // eslint-disable-next-line + salesforceQuery = sinon.stub(SalesforceService, 'queryBillingAccount', () => Promise.resolve(billingAccountData)); + done(); + }); + }); + + afterEach((done) => { + salesforceAuthenticate.restore(); + salesforceQuery.restore(); + done(); + }); + + after((done) => { + testUtil.clearDb(done); + }); + + describe('Get /projects/{id}/billingAccounts', () => { + it('should return 403 for anonymous user', (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .expect(403, done); + }); + + it('should return 403 for admin', (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send() + .expect(403, done); + }); + + it('should return 404 if the project is not found', (done) => { + request(server) + .get('/v5/projects/11223344/billingAccount') + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(404, done); + }); + + it('should return 404 if billing account is not defined in the project', (done) => { + request(server) + .get(`/v5/projects/${project2.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(404, done); + }); + + it('should return billing account details using M2M token with "read:project-billing-account-details" scope', + (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + resJson.should.deep.equal(billingAccountData); + done(); + } + }); + }); + }); +}); diff --git a/src/tests/util.js b/src/tests/util.js index 85a4bc64..449f1c86 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -41,6 +41,7 @@ export default { [M2M_SCOPES.CONNECT_PROJECT_ADMIN]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOmNvbm5lY3RfcHJvamVjdCIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.q34b2IC1pw3ksl5RtnSEW5_HGwN0asx2MD3LV9-Wffg', // TODO update token to have correct scope, as of now it is copied from CONNECT_PROJECT_ADMIN [M2M_SCOPES.PROJECTS.READ_USER_BILLING_ACCOUNTS]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOmNvbm5lY3RfcHJvamVjdCIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.q34b2IC1pw3ksl5RtnSEW5_HGwN0asx2MD3LV9-Wffg', + [M2M_SCOPES.PROJECTS.READ_PROJECT_BILLING_ACCOUNT_DETAILS]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoicmVhZDpwcm9qZWN0LWJpbGxpbmctYWNjb3VudC1kZXRhaWxzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.1uuT0zg9vSjyOaEZzpeWtjAiRiCJDXvzUEZ4B2cIj1Q', [M2M_SCOPES.PROJECTS.ALL]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOnByb2plY3RzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.ixFXMCsBmIN9mQ9Z3s-Apkg20A3d86Pm9RouL7bZMV4', [M2M_SCOPES.PROJECTS.READ]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoicmVhZDpwcm9qZWN0cyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.IpYgfbem-eR6tGjBoxQBPDw6YIulBTZLBn48NuyJT_g', [M2M_SCOPES.PROJECTS.WRITE]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoid3JpdGU6cHJvamVjdHMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.cAMbmnSKXB8Xl4s4Nlo1LduPySBcvKz2Ygilq5b0OD0', From e1664e362c4964f2eeccfbdaeb81692e0f1f42eb Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 10 Mar 2021 13:02:42 +0200 Subject: [PATCH 6/7] fix: billing account data format ref issue #636 --- src/routes/billingAccounts/get.spec.js | 5 +---- src/services/salesforceService.js | 6 +++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/routes/billingAccounts/get.spec.js b/src/routes/billingAccounts/get.spec.js index 2171ee0d..09496fcb 100644 --- a/src/routes/billingAccounts/get.spec.js +++ b/src/routes/billingAccounts/get.spec.js @@ -12,11 +12,8 @@ chai.should(); // demo data which might be returned by the `SalesforceService.query` const billingAccountData = { - sfBillingAccountId: '123', tcBillingAccountId: 123123, - name: 'Billing Account 1', - startDate: '2021-02-10T18:51:27Z', - endDate: '2021-03-10T18:51:27Z', + markup: '12', }; describe('Project Billing Accounts list', () => { diff --git a/src/services/salesforceService.js b/src/services/salesforceService.js index efb59c6a..2ddd65af 100644 --- a/src/services/salesforceService.js +++ b/src/services/salesforceService.js @@ -96,7 +96,11 @@ class SalesforceService { logger.debug(_.get(res, 'data.records', [])); } const billingAccounts = _.get(res, 'data.records', []).map(o => ({ - tcBillingAccountId: _.get(o, 'TopCoder_Billing_Account_Id__c'), + tcBillingAccountId: util.parseIntStrictly( + _.get(o, 'TopCoder_Billing_Account_Id__c'), + 10, + null, // fallback to null if cannot parse + ), markup: _.get(o, 'Mark_Up__c'), })); return billingAccounts.length > 0 ? billingAccounts[0] : {}; From e3ca0b442a0a85619b34043d7310785f25f1a4ef Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 10 Mar 2021 13:31:14 +0200 Subject: [PATCH 7/7] fix: markup test data as integer ref issue #636 --- src/routes/billingAccounts/get.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/billingAccounts/get.spec.js b/src/routes/billingAccounts/get.spec.js index 09496fcb..bbcd41af 100644 --- a/src/routes/billingAccounts/get.spec.js +++ b/src/routes/billingAccounts/get.spec.js @@ -13,7 +13,7 @@ chai.should(); // demo data which might be returned by the `SalesforceService.query` const billingAccountData = { tcBillingAccountId: 123123, - markup: '12', + markup: 50, }; describe('Project Billing Accounts list', () => {