From d8c6102b33a6b7f370c02a0162ce61b5db08c373 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 6 May 2020 11:00:38 +0300 Subject: [PATCH] refactor: reenable lint rules and apply fixes --- .eslintrc | 11 +- scripts/data/export/index.js | 38 +- scripts/es-db-compare/index.js | 6 +- src/constants.js | 2 +- src/events/busApi.js | 378 +++++----- src/events/milestones/index.js | 4 +- src/events/phaseProducts/index.js | 12 +- src/events/projectAttachments/index.js | 12 +- src/events/projectMembers/index.js | 10 +- src/events/projectPhases/index.js | 2 +- src/events/projects/index.js | 4 +- src/events/timelines/index.js | 2 +- src/mocks/direct.js | 102 +-- src/models/milestone.js | 54 +- src/models/project.js | 24 +- src/models/projectAttachment.js | 4 +- src/models/timeline.js | 10 +- src/permissions/constants.js | 2 +- src/permissions/generalPermission.js | 2 + src/permissions/project.downloadAttachment.js | 26 +- src/permissions/project.edit.js | 24 +- src/permissions/project.updateAttachment.js | 26 +- src/permissions/project.view.js | 32 +- src/routes/admin/project-index-create.js | 50 +- src/routes/admin/project-index-delete.js | 63 +- src/routes/attachments/create.js | 62 +- src/routes/attachments/create.spec.js | 196 ++--- src/routes/attachments/delete.js | 58 +- src/routes/attachments/delete.spec.js | 264 +++---- src/routes/attachments/get.js | 74 +- src/routes/attachments/get.spec.js | 78 +- src/routes/attachments/list.js | 34 +- src/routes/attachments/list.spec.js | 92 +-- src/routes/attachments/update.js | 4 +- src/routes/attachments/update.spec.js | 108 +-- src/routes/form/revision/create.js | 2 +- src/routes/form/revision/create.spec.js | 26 +- src/routes/form/revision/delete.js | 22 +- src/routes/form/revision/delete.spec.js | 84 +-- src/routes/form/revision/get.js | 56 +- src/routes/form/revision/get.spec.js | 36 +- src/routes/form/revision/list.js | 50 +- src/routes/form/revision/list.spec.js | 36 +- src/routes/form/version/create.js | 2 +- src/routes/form/version/create.spec.js | 24 +- src/routes/form/version/delete.js | 72 +- src/routes/form/version/delete.spec.js | 74 +- src/routes/form/version/get.js | 40 +- src/routes/form/version/get.spec.js | 36 +- src/routes/form/version/getVersion.js | 40 +- src/routes/form/version/getVersion.spec.js | 36 +- src/routes/form/version/list.js | 66 +- src/routes/form/version/list.spec.js | 36 +- src/routes/form/version/update.js | 42 +- src/routes/form/version/update.spec.js | 24 +- src/routes/index.js | 20 +- src/routes/metadata/list.js | 78 +- src/routes/metadata/list.spec.js | 90 +-- src/routes/milestoneTemplates/clone.spec.js | 4 +- src/routes/milestoneTemplates/create.js | 32 +- src/routes/milestoneTemplates/create.spec.js | 4 +- src/routes/milestoneTemplates/delete.js | 28 +- src/routes/milestoneTemplates/delete.spec.js | 4 +- src/routes/milestoneTemplates/get.js | 18 +- src/routes/milestoneTemplates/get.spec.js | 4 +- src/routes/milestoneTemplates/list.js | 46 +- src/routes/milestoneTemplates/list.spec.js | 4 +- src/routes/milestoneTemplates/update.spec.js | 4 +- src/routes/milestones/create.js | 72 +- src/routes/milestones/create.spec.js | 128 ++-- src/routes/milestones/delete.js | 34 +- src/routes/milestones/get.js | 44 +- src/routes/milestones/get.spec.js | 128 ++-- src/routes/milestones/update.js | 62 +- src/routes/milestones/update.spec.js | 140 ++-- src/routes/orgConfig/delete.js | 18 +- src/routes/orgConfig/delete.spec.js | 40 +- src/routes/orgConfig/get.js | 50 +- src/routes/orgConfig/list.js | 38 +- src/routes/permissions/get.spec.js | 76 +- src/routes/phaseProducts/create.js | 44 +- src/routes/phaseProducts/create.spec.js | 102 +-- src/routes/phaseProducts/delete.js | 2 +- src/routes/phaseProducts/delete.spec.js | 124 ++-- src/routes/phaseProducts/get.js | 6 +- src/routes/phaseProducts/get.spec.js | 94 +-- src/routes/phaseProducts/list.js | 10 +- src/routes/phaseProducts/update.js | 40 +- src/routes/phaseProducts/update.spec.js | 96 +-- src/routes/phases/create.js | 2 +- src/routes/phases/create.spec.js | 120 +-- src/routes/phases/delete.js | 2 +- src/routes/phases/delete.spec.js | 178 ++--- src/routes/phases/get.js | 44 +- src/routes/phases/get.spec.js | 72 +- src/routes/phases/update.spec.js | 618 +++++++-------- src/routes/planConfig/revision/create.js | 2 +- src/routes/planConfig/revision/create.spec.js | 26 +- src/routes/planConfig/revision/delete.js | 34 +- src/routes/planConfig/revision/delete.spec.js | 86 +-- src/routes/planConfig/revision/get.js | 54 +- src/routes/planConfig/revision/get.spec.js | 36 +- src/routes/planConfig/revision/list.js | 56 +- src/routes/planConfig/revision/list.spec.js | 36 +- src/routes/planConfig/version/create.js | 2 +- src/routes/planConfig/version/create.spec.js | 24 +- src/routes/planConfig/version/delete.js | 74 +- src/routes/planConfig/version/delete.spec.js | 74 +- src/routes/planConfig/version/get.js | 42 +- src/routes/planConfig/version/get.spec.js | 38 +- src/routes/planConfig/version/getVersion.js | 76 +- .../planConfig/version/getVersion.spec.js | 36 +- src/routes/planConfig/version/list.js | 66 +- src/routes/planConfig/version/list.spec.js | 36 +- src/routes/planConfig/version/update.js | 42 +- src/routes/planConfig/version/update.spec.js | 24 +- src/routes/priceConfig/revision/create.js | 2 +- .../priceConfig/revision/create.spec.js | 26 +- src/routes/priceConfig/revision/delete.js | 34 +- .../priceConfig/revision/delete.spec.js | 84 +-- src/routes/priceConfig/revision/get.js | 54 +- src/routes/priceConfig/revision/get.spec.js | 36 +- src/routes/priceConfig/revision/list.js | 56 +- src/routes/priceConfig/revision/list.spec.js | 36 +- src/routes/priceConfig/version/create.js | 2 +- src/routes/priceConfig/version/create.spec.js | 24 +- src/routes/priceConfig/version/delete.js | 74 +- src/routes/priceConfig/version/delete.spec.js | 74 +- src/routes/priceConfig/version/get.js | 42 +- src/routes/priceConfig/version/get.spec.js | 36 +- src/routes/priceConfig/version/getVersion.js | 42 +- .../priceConfig/version/getVersion.spec.js | 36 +- src/routes/priceConfig/version/list.js | 66 +- src/routes/priceConfig/version/list.spec.js | 36 +- src/routes/priceConfig/version/update.js | 42 +- src/routes/priceConfig/version/update.spec.js | 24 +- src/routes/productCategories/delete.js | 18 +- src/routes/productCategories/delete.spec.js | 40 +- src/routes/productCategories/get.js | 50 +- src/routes/productCategories/list.js | 30 +- src/routes/productTemplates/create.js | 4 +- src/routes/productTemplates/delete.js | 20 +- src/routes/productTemplates/delete.spec.js | 40 +- src/routes/productTemplates/get.js | 54 +- src/routes/productTemplates/list.js | 50 +- src/routes/productTemplates/list.spec.js | 14 +- src/routes/productTemplates/update.js | 4 +- src/routes/productTemplates/update.spec.js | 56 +- src/routes/productTemplates/upgrade.spec.js | 122 +-- src/routes/projectMemberInvites/create.js | 244 +++--- .../projectMemberInvites/create.spec.js | 238 +++--- .../projectMemberInvites/delete.spec.js | 46 +- src/routes/projectMemberInvites/get.js | 4 +- src/routes/projectMemberInvites/get.spec.js | 250 +++---- src/routes/projectMemberInvites/list.spec.js | 314 ++++---- src/routes/projectMemberInvites/update.js | 12 +- .../projectMemberInvites/update.spec.js | 118 +-- src/routes/projectMembers/create.spec.js | 328 ++++---- src/routes/projectMembers/delete.js | 132 ++-- src/routes/projectMembers/delete.spec.js | 28 +- src/routes/projectMembers/get.js | 84 +-- src/routes/projectMembers/get.spec.js | 62 +- src/routes/projectMembers/list.js | 90 +-- src/routes/projectMembers/list.spec.js | 36 +- src/routes/projectMembers/update.js | 170 ++--- src/routes/projectMembers/update.spec.js | 66 +- src/routes/projectReports/getEmbedReport.js | 20 +- src/routes/projectReports/getReport.spec.js | 2 +- src/routes/projectSettings/create.js | 2 +- src/routes/projectSettings/create.spec.js | 70 +- src/routes/projectSettings/delete.js | 44 +- src/routes/projectSettings/delete.spec.js | 248 +++---- src/routes/projectSettings/list.spec.js | 46 +- src/routes/projectSettings/update.js | 42 +- src/routes/projectSettings/update.spec.js | 234 +++--- src/routes/projectTemplates/create.js | 8 +- src/routes/projectTemplates/delete.js | 22 +- src/routes/projectTemplates/delete.spec.js | 40 +- src/routes/projectTemplates/get.js | 56 +- src/routes/projectTemplates/list.js | 38 +- src/routes/projectTemplates/update.js | 32 +- src/routes/projectTemplates/update.spec.js | 106 +-- src/routes/projectTemplates/upgrade.js | 2 +- src/routes/projectTemplates/upgrade.spec.js | 24 +- src/routes/projectTypes/delete.js | 20 +- src/routes/projectTypes/delete.spec.js | 40 +- src/routes/projectTypes/get.js | 50 +- src/routes/projectTypes/list.js | 30 +- src/routes/projects/create.js | 368 ++++----- src/routes/projects/delete.js | 26 +- src/routes/projects/delete.spec.js | 42 +- src/routes/projects/get.js | 38 +- src/routes/projects/get.spec.js | 702 +++++++++--------- src/routes/projects/list.js | 134 ++-- src/routes/projects/list.spec.js | 426 +++++------ src/routes/projects/update.js | 8 +- src/routes/projects/update.spec.js | 536 ++++++------- src/routes/scopeChangeRequests/create.js | 68 +- src/routes/scopeChangeRequests/update.js | 84 +-- src/routes/timelines/create.js | 52 +- src/routes/timelines/delete.js | 44 +- src/routes/timelines/delete.spec.js | 40 +- src/routes/timelines/get.js | 22 +- src/routes/timelines/get.spec.js | 2 +- src/routes/timelines/list.js | 28 +- src/routes/timelines/list.spec.js | 62 +- src/routes/workItems/create.js | 104 +-- src/routes/workItems/create.spec.js | 182 ++--- src/routes/workItems/delete.js | 74 +- src/routes/workItems/delete.spec.js | 230 +++--- src/routes/workItems/get.js | 46 +- src/routes/workItems/get.spec.js | 106 +-- src/routes/workItems/list.js | 30 +- src/routes/workItems/list.spec.js | 102 +-- src/routes/workItems/update.js | 88 +-- src/routes/workItems/update.spec.js | 150 ++-- .../workManagementPermissions/create.spec.js | 8 +- .../workManagementPermissions/delete.js | 10 +- .../workManagementPermissions/delete.spec.js | 124 ++-- .../workManagementPermissions/get.spec.js | 16 +- src/routes/workManagementPermissions/list.js | 8 +- .../workManagementPermissions/list.spec.js | 14 +- .../workManagementPermissions/update.js | 48 +- .../workManagementPermissions/update.spec.js | 18 +- src/routes/workStreams/create.spec.js | 6 +- src/routes/workStreams/delete.js | 10 +- src/routes/workStreams/delete.spec.js | 66 +- src/routes/workStreams/get.spec.js | 28 +- src/routes/workStreams/list.js | 32 +- src/routes/workStreams/list.spec.js | 8 +- src/routes/workStreams/update.js | 16 +- src/routes/workStreams/update.spec.js | 76 +- src/routes/works/create.js | 36 +- src/routes/works/create.spec.js | 240 +++--- src/routes/works/delete.js | 92 +-- src/routes/works/delete.spec.js | 236 +++--- src/routes/works/get.spec.js | 86 +-- src/routes/works/list.js | 36 +- src/routes/works/list.spec.js | 62 +- src/routes/works/update.js | 60 +- src/routes/works/update.spec.js | 690 ++++++++--------- src/services/index.js | 14 +- src/services/messageService.js | 16 +- src/tests/util.js | 12 +- src/util.js | 166 +++-- 245 files changed, 8519 insertions(+), 8493 deletions(-) diff --git a/.eslintrc b/.eslintrc index 06a6152f..025c59d8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,15 +10,18 @@ "rules": { "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js", "src/tests/*.js"]}], "max-len": ["error", { "ignoreComments": true, "code": 120 }], + "valid-jsdoc": ["error", { + "requireReturn": true, + "requireReturnType": true, + "requireParamDescription": true, + "requireReturnDescription": true + }], "require-jsdoc": ["error", { "require": { "FunctionDeclaration": true, "MethodDefinition": true, "ClassDeclaration": true } - }], - "indent": 0, - "no-multi-spaces": 0, - "valid-jsdoc": 0 + }] } } diff --git a/scripts/data/export/index.js b/scripts/data/export/index.js index 67687b23..95b4df13 100644 --- a/scripts/data/export/index.js +++ b/scripts/data/export/index.js @@ -31,26 +31,26 @@ logger.info('Script will export data to file:', filePath); // check if file exists if (fs.existsSync(filePath)) { // We delay question for overwrite file, because the question overlaps with a warning message from sequelize module -Promise.delay(1).then(() => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, + Promise.delay(1).then(() => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + // confirm overwritting to file + rl.question( + 'File already exists, Are you sure to overwrite it? [Y] to overwrite: ', + (answer) => { + rl.close(); + if (answer.toLowerCase() === 'y') { + logger.info('File will be overwritten.'); + runExportData(filePath, logger); + } else { + logger.info('Exit without exporting any data'); + process.exit(0); + } + }, + ); // question() }); - // confirm overwritting to file - rl.question( - 'File already exists, Are you sure to overwrite it? [Y] to overwrite: ', - (answer) => { - rl.close(); - if (answer.toLowerCase() === 'y') { - logger.info('File will be overwritten.'); - runExportData(filePath, logger); - } else { - logger.info('Exit without exporting any data'); - process.exit(0); - } - }, - ); // question() -}); } else { // get base directory of the file const baseDir = path.resolve(filePath, '..'); diff --git a/scripts/es-db-compare/index.js b/scripts/es-db-compare/index.js index 8d4a56f4..7b4ef527 100644 --- a/scripts/es-db-compare/index.js +++ b/scripts/es-db-compare/index.js @@ -147,7 +147,7 @@ async function getProductTimelinesFromES() { }; return es.search(searchCriteria) .then((docs) => { - const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle + const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle return rows; }); } @@ -161,7 +161,7 @@ async function getProjectsFromES() { const searchCriteria = getESSearchCriteriaForProject(); const projects = await es.search(searchCriteria) .then((docs) => { - const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle + const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle return rows; }); const timelines = await getProductTimelinesFromES(); @@ -188,7 +188,7 @@ async function getMetadataFromES() { }; return es.search(searchCriteria) .then((docs) => { - const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle + const rows = lodash.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle if (!rows.length) { return lodash.reduce( Object.keys(scriptConstants.associations.metadata), diff --git a/src/constants.js b/src/constants.js index 1babbe14..9231bc66 100644 --- a/src/constants.js +++ b/src/constants.js @@ -243,7 +243,7 @@ export const CONNECT_NOTIFICATION_EVENT = { MILESTONE_TRANSITION_ACTIVE: 'connect.notification.project.timeline.milestone.transition.active', // When milestone is marked as completed MILESTONE_TRANSITION_COMPLETED: 'connect.notification.project.timeline.milestone.transition.completed', - // When milestone is marked as paused + // When milestone is marked as paused MILESTONE_TRANSITION_PAUSED: 'connect.notification.project.timeline.milestone.transition.paused', // When milestone is waiting for customers's input MILESTONE_WAITING_CUSTOMER: 'connect.notification.project.timeline.milestone.waiting.customer', diff --git a/src/events/busApi.js b/src/events/busApi.js index a6af87c6..36bd5dd8 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -129,7 +129,7 @@ module.exports = (app, logger) => { // send PROJECT_UPDATED Kafka message when one of the specified below properties changed const watchProperties = ['status', 'details', 'name', 'description', 'bookmarks']; if (!_.isEqual(_.pick(original, watchProperties), - _.pick(updated, watchProperties))) { + _.pick(updated, watchProperties))) { createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, { projectId: updated.id, projectName: updated.name, @@ -153,7 +153,7 @@ module.exports = (app, logger) => { /** * PROJECT_METADATA_CREATE */ - app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_METADATA_CREATE event'); // send event to bus api @@ -163,7 +163,7 @@ module.exports = (app, logger) => { /** * PROJECT_METADATA_UPDATE */ - app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_UPDATE, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_UPDATE, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_METADATA_UPDATE event'); createEvent(BUS_API_EVENT.PROJECT_METADATA_UPDATE, resource, logger); @@ -172,7 +172,7 @@ module.exports = (app, logger) => { /** * PROJECT_METADATA_DELETE */ - app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_METADATA_DELETE event'); createEvent(BUS_API_EVENT.PROJECT_METADATA_DELETE, resource, logger); @@ -227,7 +227,7 @@ module.exports = (app, logger) => { userId: member.userId, initiatorUserId: req.authUser.userId, }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -273,7 +273,7 @@ module.exports = (app, logger) => { initiatorUserId: req.authUser.userId, }, logger); } - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -316,7 +316,7 @@ module.exports = (app, logger) => { initiatorUserId: req.authUser.userId, }, logger); } - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -370,7 +370,7 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -389,16 +389,16 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_ATTACHMENT_UPDATED, { - projectId: project.id, - projectName: project.name, - refCode: _.get(project, 'details.utm.code'), - projectUrl: connectProjectUrl(project.id), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars + .then((project) => { + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_ATTACHMENT_UPDATED, { + projectId: project.id, + projectName: project.name, + refCode: _.get(project, 'details.utm.code'), + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -417,16 +417,16 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_ATTACHMENT_UPDATED, { - projectId: project.id, - projectName: project.name, - refCode: _.get(project, 'details.utm.code'), - projectUrl: connectProjectUrl(project.id), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars + .then((project) => { + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_ATTACHMENT_UPDATED, { + projectId: project.id, + projectName: project.name, + refCode: _.get(project, 'details.utm.code'), + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -488,7 +488,7 @@ module.exports = (app, logger) => { util.getTopcoderProjectMembers(project.members) : null, }, logger); return sendPlanReadyEventIfNeeded(req, project, created); - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -517,9 +517,9 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, allowedUsers: deleted.status === PROJECT_PHASE_STATUS.DRAFT ? - util.getTopcoderProjectMembers(project.members) : null, + util.getTopcoderProjectMembers(project.members) : null, }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -549,27 +549,27 @@ module.exports = (app, logger) => { ['duration', CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED], ['startDate', CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED], ['spentBudget', route === ROUTES.PHASES.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PAYMENT - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PAYMENT, + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PAYMENT + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PAYMENT, ], ['progress', [route === ROUTES.PHASES.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PROGRESS - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PROGRESS, - CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED, + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PROGRESS + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PROGRESS, + CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED, ]], ['details', route === ROUTES.PHASES.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_SCOPE - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_SCOPE, + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_SCOPE + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_SCOPE, ], ['status', route === ROUTES.PHASES.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_ACTIVE, - PROJECT_PHASE_STATUS.ACTIVE, + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_ACTIVE, + PROJECT_PHASE_STATUS.ACTIVE, ], ['status', route === ROUTES.PHASES.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_COMPLETED, - PROJECT_PHASE_STATUS.COMPLETED, + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_COMPLETED, + PROJECT_PHASE_STATUS.COMPLETED, ], // ideally we should validate the old value being 'DRAFT' but there is no other status from which // we can move phase to REVIEWED status @@ -596,14 +596,14 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, allowedUsers: updated.status === PROJECT_PHASE_STATUS.DRAFT ? - util.getTopcoderProjectMembers(project.members) : null, + util.getTopcoderProjectMembers(project.members) : null, }, logger)); events.forEach((event) => { eventsMap[event] = true; }); } }); return sendPlanReadyEventIfNeeded(req, project, updated); - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars } }); @@ -677,7 +677,7 @@ module.exports = (app, logger) => { /** * MILESTONE_ADDED. */ - app.on(EVENT.ROUTING_KEY.MILESTONE_ADDED, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.MILESTONE_ADDED, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive MILESTONE_ADDED event'); createEvent(BUS_API_EVENT.MILESTONE_ADDED, resource, logger); @@ -705,7 +705,7 @@ module.exports = (app, logger) => { } // sendMilestoneNotification(req, {}, created, project); }) - .catch(err => null); // eslint-disable-line no-unused-vars + .catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -717,7 +717,7 @@ module.exports = (app, logger) => { originalResource, cascadedUpdates, skipNotification, - }) => { // eslint-disable-line no-unused-vars + }) => { // eslint-disable-line no-unused-vars logger.debug(`receive MILESTONE_UPDATED event for milestone ${resource.id}`); createEvent(BUS_API_EVENT.MILESTONE_UPDATED, resource, logger); @@ -734,48 +734,48 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - logger.debug(`Found project with id ${projectId}`); - return models.Milestone.getTimelineDuration(timeline.id) - .then(({ duration, progress }) => { - timeline.duration = duration; - timeline.progress = progress; - sendMilestoneNotification(req, original, updated, project, timeline); - - logger.debug('cascadedUpdates', cascadedUpdates); - if (cascadedUpdates && cascadedUpdates.milestones && cascadedUpdates.milestones.length > 0) { - _.each(cascadedUpdates.milestones, cascadedUpdate => - sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project, timeline), - ); - } - - // if timeline is modified - if (cascadedUpdates && cascadedUpdates.timeline) { - const cTimeline = cascadedUpdates.timeline; - // if endDate of the timeline is modified, raise TIMELINE_ADJUSTED event - if (!moment(cTimeline.original.endDate).isSame(cTimeline.updated.endDate)) { - // Raise Timeline changed event - createEvent(CONNECT_NOTIFICATION_EVENT.TIMELINE_ADJUSTED, { - projectId: project.id, - projectName: project.name, - refCode: _.get(project, 'details.utm.code'), - projectUrl: connectProjectUrl(project.id), - originalTimeline: cTimeline.original, - updatedTimeline: cTimeline.updated, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - } - } - }); - }).catch(err => null); // eslint-disable-line no-unused-vars + .then((project) => { + logger.debug(`Found project with id ${projectId}`); + return models.Milestone.getTimelineDuration(timeline.id) + .then(({ duration, progress }) => { + timeline.duration = duration; + timeline.progress = progress; + sendMilestoneNotification(req, original, updated, project, timeline); + + logger.debug('cascadedUpdates', cascadedUpdates); + if (cascadedUpdates && cascadedUpdates.milestones && cascadedUpdates.milestones.length > 0) { + _.each(cascadedUpdates.milestones, cascadedUpdate => + sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project, timeline), + ); + } + + // if timeline is modified + if (cascadedUpdates && cascadedUpdates.timeline) { + const cTimeline = cascadedUpdates.timeline; + // if endDate of the timeline is modified, raise TIMELINE_ADJUSTED event + if (!moment(cTimeline.original.endDate).isSame(cTimeline.updated.endDate)) { + // Raise Timeline changed event + createEvent(CONNECT_NOTIFICATION_EVENT.TIMELINE_ADJUSTED, { + projectId: project.id, + projectName: project.name, + refCode: _.get(project, 'details.utm.code'), + projectUrl: connectProjectUrl(project.id), + originalTimeline: cTimeline.original, + updatedTimeline: cTimeline.updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + } + }); + }).catch(err => null); // eslint-disable-line no-unused-vars } }); - /** + /** * MILESTONE_REMOVED. */ - app.on(EVENT.ROUTING_KEY.MILESTONE_REMOVED, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.MILESTONE_REMOVED, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive MILESTONE_REMOVED event'); createEvent(BUS_API_EVENT.MILESTONE_REMOVED, resource, logger); @@ -789,19 +789,19 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - if (project) { - createEvent(CONNECT_NOTIFICATION_EVENT.MILESTONE_REMOVED, { - projectId, - projectName: project.name, - refCode: _.get(project, 'details.utm.code'), - projectUrl: connectProjectUrl(projectId), - removedMilestone: deleted, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - } - }).catch(err => null); // eslint-disable-line no-unused-vars + .then((project) => { + if (project) { + createEvent(CONNECT_NOTIFICATION_EVENT.MILESTONE_REMOVED, { + projectId, + projectName: project.name, + refCode: _.get(project, 'details.utm.code'), + projectUrl: connectProjectUrl(projectId), + removedMilestone: deleted, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -822,7 +822,7 @@ module.exports = (app, logger) => { createEvent(BUS_API_EVENT.MILESTONE_TEMPLATE_UPDATED, resource, logger); }); - /** + /** * MILESTONE_TEMPLATE_REMOVED. */ app.on(EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_REMOVED, ({ req, resource }) => { // eslint-disable-line no-unused-vars @@ -865,27 +865,27 @@ module.exports = (app, logger) => { // send PROJECT_UPDATED Kafka message when one of the specified below properties changed const watchProperties = ['startDate', 'endDate']; if (!_.isEqual(_.pick(original, watchProperties), - _.pick(updated, watchProperties))) { + _.pick(updated, watchProperties))) { // req.params.projectId is set by validateTimelineIdParam middleware const projectId = _.parseInt(req.params.projectId); models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - if (project) { - createEvent(CONNECT_NOTIFICATION_EVENT.TIMELINE_ADJUSTED, { - projectId, - projectName: project.name, - refCode: _.get(project, 'details.utm.code'), - projectUrl: connectProjectUrl(projectId), - originalTimeline: original, - updatedTimeline: updated, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - } - }).catch(err => null); // eslint-disable-line no-unused-vars + .then((project) => { + if (project) { + createEvent(CONNECT_NOTIFICATION_EVENT.TIMELINE_ADJUSTED, { + projectId, + projectName: project.name, + refCode: _.get(project, 'details.utm.code'), + projectUrl: connectProjectUrl(projectId), + originalTimeline: original, + updatedTimeline: updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars } }); @@ -931,8 +931,8 @@ module.exports = (app, logger) => { logger.debug(`Spec changed for product id ${updated.id}`); const busApiEvent = route === ROUTES.PHASE_PRODUCTS.UPDATE - ? CONNECT_NOTIFICATION_EVENT.PROJECT_PRODUCT_SPECIFICATION_MODIFIED - : CONNECT_NOTIFICATION_EVENT.PROJECT_WORKITEM_SPECIFICATION_MODIFIED; + ? CONNECT_NOTIFICATION_EVENT.PROJECT_PRODUCT_SPECIFICATION_MODIFIED + : CONNECT_NOTIFICATION_EVENT.PROJECT_WORKITEM_SPECIFICATION_MODIFIED; createEvent(busApiEvent, { projectId, @@ -946,7 +946,7 @@ module.exports = (app, logger) => { const watchProperties = ['name', 'estimatedPrice', 'actualPrice', 'details']; if (!_.isEqual(_.pick(original, watchProperties), - _.pick(updated, watchProperties))) { + _.pick(updated, watchProperties))) { createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, { projectId, projectName: project.name, @@ -955,16 +955,16 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, allowedUsers: updated.status === PROJECT_PHASE_STATUS.DRAFT ? - util.getTopcoderProjectMembers(project.members) : null, + util.getTopcoderProjectMembers(project.members) : null, }, logger); } - }).catch(err => null); // eslint-disable-line no-unused-vars + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** * PROJECT_MEMBER_INVITE_CREATED */ - app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_MEMBER_INVITE_CREATED event'); createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, resource, logger); @@ -981,43 +981,43 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - logger.debug(util.isSSO); - if (status === INVITE_STATUS.REQUESTED) { - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, { - projectId, - userId, - email, - role, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }, logger); - } else { + .then((project) => { + logger.debug(util.isSSO); + if (status === INVITE_STATUS.REQUESTED) { + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, { + projectId, + userId, + email, + role, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }, logger); + } else { // send event to bus api - logger.debug({ - projectId, - userId, - email, - role, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }); - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, { - projectId, - userId, - email, - role, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }, logger); - } - }).catch(err => logger.error(err)); // eslint-disable-line no-unused-vars + logger.debug({ + projectId, + userId, + email, + role, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }); + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, { + projectId, + userId, + email, + role, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }, logger); + } + }).catch(err => logger.error(err)); // eslint-disable-line no-unused-vars }); /** * PROJECT_MEMBER_INVITE_UPDATED */ - app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_MEMBER_INVITE_UPDATED event'); createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, resource, logger); @@ -1035,51 +1035,51 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - logger.debug(util.isSSO); - if (status === INVITE_STATUS.REQUEST_APPROVED) { + .then((project) => { + logger.debug(util.isSSO); + if (status === INVITE_STATUS.REQUEST_APPROVED) { // send event to bus api - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_APPROVED, { - projectId, - userId, - originator: createdBy, - email, - role, - status, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }, logger); - } else if (status === INVITE_STATUS.REQUEST_REJECTED) { + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_APPROVED, { + projectId, + userId, + originator: createdBy, + email, + role, + status, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }, logger); + } else if (status === INVITE_STATUS.REQUEST_REJECTED) { // send event to bus api - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REJECTED, { - projectId, - userId, - originator: createdBy, - email, - role, - status, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }, logger); - } else { + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REJECTED, { + projectId, + userId, + originator: createdBy, + email, + role, + status, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }, logger); + } else { // send event to bus api - createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED, { - projectId, - userId, - email, - role, - status, - initiatorUserId: req.authUser.userId, - isSSO: util.isSSO(project), - }, logger); - } - }).catch(err => null); // eslint-disable-line no-unused-vars + createEvent(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED, { + projectId, + userId, + email, + role, + status, + initiatorUserId: req.authUser.userId, + isSSO: util.isSSO(project), + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** * PROJECT_MEMBER_INVITE_REMOVED */ - app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_REMOVED, ({ req, resource }) => { // eslint-disable-line no-unused-vars + app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_REMOVED, ({ req, resource }) => { // eslint-disable-line no-unused-vars logger.debug('receive PROJECT_MEMBER_INVITE_REMOVED event'); createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REMOVED, resource, logger); diff --git a/src/events/milestones/index.js b/src/events/milestones/index.js index 8b717662..03134ebb 100644 --- a/src/events/milestones/index.js +++ b/src/events/milestones/index.js @@ -86,7 +86,7 @@ const milestoneUpdatedHandler = Promise.coroutine(function* (logger, msg, channe // if timeline has been modified during milestones updates if (data.cascadedUpdates && data.cascadedUpdates.timeline && data.cascadedUpdates.timeline.updated) { // merge updated timeline with the object in ES index, the same way as we do when updating timeline in ES using timeline endpoints - updatedTimeline = _.merge(doc._source, data.cascadedUpdates.timeline.updated); // eslint-disable-line no-underscore-dangle + updatedTimeline = _.merge(doc._source, data.cascadedUpdates.timeline.updated); // eslint-disable-line no-underscore-dangle } const merged = _.assign(updatedTimeline, { milestones }); @@ -119,7 +119,7 @@ const milestoneRemovedHandler = Promise.coroutine(function* (logger, msg, channe try { const doc = yield eClient.get({ index: ES_TIMELINE_INDEX, type: ES_TIMELINE_TYPE, id: data.timelineId }); const milestones = _.filter(doc._source.milestones, single => single.id !== data.id); // eslint-disable-line no-underscore-dangle - const merged = _.assign(doc._source, { milestones }); // eslint-disable-line no-underscore-dangle + const merged = _.assign(doc._source, { milestones }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_TIMELINE_INDEX, type: ES_TIMELINE_TYPE, diff --git a/src/events/phaseProducts/index.js b/src/events/phaseProducts/index.js index b6b6c063..3322d78e 100644 --- a/src/events/phaseProducts/index.js +++ b/src/events/phaseProducts/index.js @@ -24,7 +24,7 @@ const phaseProductAddedHandler = Promise.coroutine(function* (logger, msg, chann try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId }); - const phases = _.isArray(doc._source.phases) ? doc._source.phases : []; // eslint-disable-line no-underscore-dangle + const phases = _.isArray(doc._source.phases) ? doc._source.phases : []; // eslint-disable-line no-underscore-dangle _.each(phases, (phase) => { if (phase.id === data.phaseId) { @@ -33,7 +33,7 @@ const phaseProductAddedHandler = Promise.coroutine(function* (logger, msg, chann } }); - const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle + const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId, body: { doc: merged } }); logger.debug('phase product added to project document successfully'); channel.ack(msg); @@ -55,7 +55,7 @@ const phaseProductUpdatedHandler = Promise.coroutine(function* (logger, msg, cha try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.original.projectId }); - const phases = _.map(doc._source.phases, (phase) => { // eslint-disable-line no-underscore-dangle + const phases = _.map(doc._source.phases, (phase) => { // eslint-disable-line no-underscore-dangle if (phase.id === data.original.phaseId) { phase.products = _.map(phase.products, (product) => { // eslint-disable-line no-param-reassign if (product.id === data.original.id) { @@ -66,7 +66,7 @@ const phaseProductUpdatedHandler = Promise.coroutine(function* (logger, msg, cha } return phase; }); - const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle + const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, @@ -95,14 +95,14 @@ const phaseProductRemovedHandler = Promise.coroutine(function* (logger, msg, cha try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId }); - const phases = _.map(doc._source.phases, (phase) => { // eslint-disable-line no-underscore-dangle + const phases = _.map(doc._source.phases, (phase) => { // eslint-disable-line no-underscore-dangle if (phase.id === data.phaseId) { phase.products = _.filter(phase.products, product => product.id !== data.id); // eslint-disable-line no-param-reassign } return phase; }); - const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle + const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, diff --git a/src/events/projectAttachments/index.js b/src/events/projectAttachments/index.js index b7f100c1..c964e960 100644 --- a/src/events/projectAttachments/index.js +++ b/src/events/projectAttachments/index.js @@ -24,9 +24,9 @@ const projectAttachmentAddedHandler = Promise.coroutine(function* (logger, msg, try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId }); - const attachments = _.isArray(doc._source.attachments) ? doc._source.attachments : []; // eslint-disable-line no-underscore-dangle + const attachments = _.isArray(doc._source.attachments) ? doc._source.attachments : []; // eslint-disable-line no-underscore-dangle attachments.push(data); - const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId, body: { doc: merged } }); logger.debug('project attachment added to project document successfully'); channel.ack(msg); @@ -48,13 +48,13 @@ const projectAttachmentUpdatedHandler = Promise.coroutine(function* (logger, msg try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.original.projectId }); - const attachments = _.map(doc._source.attachments, (single) => { // eslint-disable-line no-underscore-dangle + const attachments = _.map(doc._source.attachments, (single) => { // eslint-disable-line no-underscore-dangle if (single.id === data.original.id) { return _.merge(single, data.updated); } return single; }); - const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, @@ -83,8 +83,8 @@ const projectAttachmentRemovedHandler = Promise.coroutine(function* (logger, msg try { const data = JSON.parse(msg.content.toString()); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.projectId }); - const attachments = _.filter(doc._source.attachments, single => single.id !== data.id); // eslint-disable-line no-underscore-dangle - const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle + const attachments = _.filter(doc._source.attachments, single => single.id !== data.id); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, { attachments }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, diff --git a/src/events/projectMembers/index.js b/src/events/projectMembers/index.js index 2b6868a6..b0dd780a 100644 --- a/src/events/projectMembers/index.js +++ b/src/events/projectMembers/index.js @@ -20,7 +20,7 @@ const updateESPromise = Promise.coroutine(function* a(logger, requestId, project id: projectId, body: { doc: updatedDoc }, }) - .then(() => logger.debug('elasticsearch project document updated successfully')); + .then(() => logger.debug('elasticsearch project document updated successfully')); } catch (error) { logger.error('Error caught updating ES document', error); return Promise.reject(error); @@ -76,8 +76,8 @@ const projectMemberRemovedHandler = Promise.coroutine(function* (logger, msg, ch const member = JSON.parse(msg.content.toString()); const projectId = member.projectId; const updateDocPromise = (doc) => { - const members = _.filter(doc._source.members, single => single.id !== member.id); // eslint-disable-line no-underscore-dangle - return Promise.resolve(_.set(doc._source, 'members', members)); // eslint-disable-line no-underscore-dangle + const members = _.filter(doc._source.members, single => single.id !== member.id); // eslint-disable-line no-underscore-dangle + return Promise.resolve(_.set(doc._source, 'members', members)); // eslint-disable-line no-underscore-dangle }; yield Promise.all([ updateESPromise(logger, origRequestId, projectId, updateDocPromise), @@ -107,13 +107,13 @@ const projectMemberUpdatedHandler = Promise.coroutine(function* a(logger, msg, c const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.original.projectId }); // merge the changes and update the elasticsearch index - const members = _.map(doc._source.members, (single) => { // eslint-disable-line no-underscore-dangle + const members = _.map(doc._source.members, (single) => { // eslint-disable-line no-underscore-dangle if (single.id === data.original.id) { return _.merge(single, payload); } return single; }); - const merged = _.merge(doc._source, { members }); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, { members }); // eslint-disable-line no-underscore-dangle // update the merged document yield eClient.update({ index: ES_PROJECT_INDEX, diff --git a/src/events/projectPhases/index.js b/src/events/projectPhases/index.js index ec5c21b0..bb885f4d 100644 --- a/src/events/projectPhases/index.js +++ b/src/events/projectPhases/index.js @@ -249,7 +249,7 @@ const removePhaseFromIndex = Promise.coroutine(function* (logger, msg) { // esli const phase = _.get(data, 'deleted', {}); const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: phase.projectId }); const phases = _.filter(doc._source.phases, single => single.id !== phase.id); // eslint-disable-line no-underscore-dangle - const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle + const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, diff --git a/src/events/projects/index.js b/src/events/projects/index.js index e12db136..99d5c845 100644 --- a/src/events/projects/index.js +++ b/src/events/projects/index.js @@ -115,7 +115,7 @@ const projectUpdatedHandler = Promise.coroutine(function* (logger, msg, channel) try { // first get the existing document and than merge the updated changes and save the new document const doc = yield eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: data.original.id }); - const merged = _.merge(doc._source, data.updated); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, data.updated); // eslint-disable-line no-underscore-dangle // update the merged document yield eClient.update({ index: ES_PROJECT_INDEX, @@ -198,7 +198,7 @@ async function projectUpdatedKafkaHandler(app, topic, payload) { try { const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); console.log(doc._source, 'Received project from ES');// eslint-disable-line no-underscore-dangle - const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle console.log(merged, 'Merged project'); // update the merged document await eClient.update({ diff --git a/src/events/timelines/index.js b/src/events/timelines/index.js index 39ed0636..609a5a79 100644 --- a/src/events/timelines/index.js +++ b/src/events/timelines/index.js @@ -60,7 +60,7 @@ const timelineUpdatedHandler = Promise.coroutine(function* (logger, msg, channel try { // first get the existing document and than merge the updated changes and save the new document const doc = yield eClient.get({ index: ES_TIMELINE_INDEX, type: ES_TIMELINE_TYPE, id: data.original.id }); - const merged = _.merge(doc._source, data.updated); // eslint-disable-line no-underscore-dangle + const merged = _.merge(doc._source, data.updated); // eslint-disable-line no-underscore-dangle merged.milestones = data.updated.milestones; // update the merged document yield eClient.update({ diff --git a/src/mocks/direct.js b/src/mocks/direct.js index 2b7df972..65b86280 100644 --- a/src/mocks/direct.js +++ b/src/mocks/direct.js @@ -51,65 +51,65 @@ const projects = { // Register all the routes router.route('/v3/direct/projects') - .get((req, res) => { - app.logger.info('get direct projects'); - res.json(util.wrapResponse(req.id, { projects })); - }) - .post((freq, res) => { - const req = freq; - app.logger.info({ body: req.body }, 'create direct project'); - const newId = projectId + 1; - req.body.id = newId; - projects[newId] = req.body; - res.json(util.wrapResponse(req.id, { projectId: newId })); - }); + .get((req, res) => { + app.logger.info('get direct projects'); + res.json(util.wrapResponse(req.id, { projects })); + }) + .post((freq, res) => { + const req = freq; + app.logger.info({ body: req.body }, 'create direct project'); + const newId = projectId + 1; + req.body.id = newId; + projects[newId] = req.body; + res.json(util.wrapResponse(req.id, { projectId: newId })); + }); router.route('/v3/direct/projects/:projectId(\\d+)/billingaccount') - .post((req, res) => { - const pId = req.params.projectId; - app.logger.info({ body: req.body, pId }, 'add billingaccount to Project'); - if (projects[pId]) { - projects[pId] = _.merge(projects[pId], req.body); - res.json(util.wrapResponse(req.id, { billingAccountName: 'mock account name for ' + + .post((req, res) => { + const pId = req.params.projectId; + app.logger.info({ body: req.body, pId }, 'add billingaccount to Project'); + if (projects[pId]) { + projects[pId] = _.merge(projects[pId], req.body); + res.json(util.wrapResponse(req.id, { billingAccountName: 'mock account name for ' + `${req.body.billingAccountId}` })); - } else { - res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); - } - }); + } else { + res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); + } + }); router.route('/v3/direct/projects/:projectId(\\d+)/copilot') - .post((req, res) => { - const pId = req.params.projectId; - app.logger.info({ body: req.body, pId }, 'add copilot to Project'); - if (projects[pId]) { - projects[pId] = _.merge(projects[pId], req.body); - res.json(util.wrapResponse(req.id, { copilotProjectId: pId })); - } else { - res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); - } - }) - .delete((req, res) => { - const pId = req.params.projectId; - app.logger.info({ body: req.body, pId }, 'remove copilot from Project'); - if (projects[pId]) { - projects[pId] = _.omit(projects[pId], 'copilotUserId'); - res.json(util.wrapResponse(req.id, true)); - } else { - res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); - } - }); + .post((req, res) => { + const pId = req.params.projectId; + app.logger.info({ body: req.body, pId }, 'add copilot to Project'); + if (projects[pId]) { + projects[pId] = _.merge(projects[pId], req.body); + res.json(util.wrapResponse(req.id, { copilotProjectId: pId })); + } else { + res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); + } + }) + .delete((req, res) => { + const pId = req.params.projectId; + app.logger.info({ body: req.body, pId }, 'remove copilot from Project'); + if (projects[pId]) { + projects[pId] = _.omit(projects[pId], 'copilotUserId'); + res.json(util.wrapResponse(req.id, true)); + } else { + res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); + } + }); router.route('/v3/direct/projects/:projectId(\\d+)/permissions') - .post((req, res) => { - const pId = req.params.projectId; - app.logger.info({ body: req.body, pId }, 'add permissions to Project'); - if (projects[pId]) { - res.json(); - } else { - res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); - } - }); + .post((req, res) => { + const pId = req.params.projectId; + app.logger.info({ body: req.body, pId }, 'add permissions to Project'); + if (projects[pId]) { + res.json(); + } else { + res.json(util.wrapErrorResponse(req.id, 404, `Cannot find direct project ${pId}`)); + } + }); app.use(router); diff --git a/src/models/milestone.js b/src/models/milestone.js index 644f7b0c..2e242cd2 100644 --- a/src/models/milestone.js +++ b/src/models/milestone.js @@ -146,37 +146,37 @@ module.exports = (sequelize, DataTypes) => { attributes: ['id', 'duration', 'startDate', 'endDate', 'actualStartDate', 'completionDate'], raw: true, }) - .then((milestones) => { - let scheduledDuration = 0; - let completedDuration = 0; - let duration = 0; - let progress = 0; - if (milestones) { - const fMilestone = milestones[0]; - const lMilestone = milestones[milestones.length - 1]; - const startDate = fMilestone.actualStartDate ? fMilestone.actualStartDate : fMilestone.startDate; - const endDate = lMilestone.completionDate ? lMilestone.completionDate : lMilestone.endDate; - duration = moment.utc(endDate).diff(moment.utc(startDate), 'days') + 1; - milestones.forEach((m) => { - if (m.completionDate !== null) { - let mDuration = 0; - if (m.actualStartDate !== null) { - mDuration = moment.utc(m.completionDate).diff(moment.utc(m.actualStartDate), 'days') + 1; + .then((milestones) => { + let scheduledDuration = 0; + let completedDuration = 0; + let duration = 0; + let progress = 0; + if (milestones) { + const fMilestone = milestones[0]; + const lMilestone = milestones[milestones.length - 1]; + const startDate = fMilestone.actualStartDate ? fMilestone.actualStartDate : fMilestone.startDate; + const endDate = lMilestone.completionDate ? lMilestone.completionDate : lMilestone.endDate; + duration = moment.utc(endDate).diff(moment.utc(startDate), 'days') + 1; + milestones.forEach((m) => { + if (m.completionDate !== null) { + let mDuration = 0; + if (m.actualStartDate !== null) { + mDuration = moment.utc(m.completionDate).diff(moment.utc(m.actualStartDate), 'days') + 1; + } else { + mDuration = moment.utc(m.completionDate).diff(moment.utc(m.startDate), 'days') + 1; + } + scheduledDuration += mDuration; + completedDuration += mDuration; } else { - mDuration = moment.utc(m.completionDate).diff(moment.utc(m.startDate), 'days') + 1; + scheduledDuration += m.duration; } - scheduledDuration += mDuration; - completedDuration += mDuration; - } else { - scheduledDuration += m.duration; + }); + if (scheduledDuration > 0) { + progress = Math.round((completedDuration / scheduledDuration) * 100); } - }); - if (scheduledDuration > 0) { - progress = Math.round((completedDuration / scheduledDuration) * 100); } - } - return Promise.resolve({ duration, progress }); - }); + return Promise.resolve({ duration, progress }); + }); }; return Milestone; diff --git a/src/models/project.js b/src/models/project.js index e027c160..9d3b39f4 100644 --- a/src/models/project.js +++ b/src/models/project.js @@ -79,7 +79,7 @@ module.exports = function defineProject(sequelize, DataTypes) { attributes: ['directProjectId'], raw: true, }) - .then(res => res.directProjectId); + .then(res => res.directProjectId); /** @@ -167,11 +167,11 @@ module.exports = function defineProject(sequelize, DataTypes) { return sequelize.query(`SELECT COUNT(1) FROM projects AS projects ${joinQuery} WHERE ${query}`, - { type: sequelize.QueryTypes.SELECT, - replacements, - logging: (str) => { log.debug(str); }, - raw: true, - }) + { type: sequelize.QueryTypes.SELECT, + replacements, + logging: (str) => { log.debug(str); }, + raw: true, + }) .then((fcount) => { let count = fcount.length; if (fcount.length === 1) { @@ -185,11 +185,11 @@ module.exports = function defineProject(sequelize, DataTypes) { ${joinQuery} WHERE ${query} ORDER BY ` + ` projects.${orderStr} LIMIT :limit OFFSET :offset`, - { type: sequelize.QueryTypes.SELECT, - replacements, - logging: (str) => { log.debug(str); }, - raw: true, - }) + { type: sequelize.QueryTypes.SELECT, + replacements, + logging: (str) => { log.debug(str); }, + raw: true, + }) .then(projects => ({ rows: projects, count })); }); }; @@ -202,7 +202,7 @@ module.exports = function defineProject(sequelize, DataTypes) { model: models.ProjectPhase, as: 'phases', order: [['startDate', 'asc']], - // where: phasesWhere, + // where: phasesWhere, include: [{ model: models.PhaseProduct, as: 'products', diff --git a/src/models/projectAttachment.js b/src/models/projectAttachment.js index 42abfa7c..fc34ecbc 100644 --- a/src/models/projectAttachment.js +++ b/src/models/projectAttachment.js @@ -59,8 +59,8 @@ module.exports = function defineProjectAttachment(sequelize, DataTypes) { }, { allowedUsers: { $or: [ - { $contains: [userId] }, - { $eq: null }, + { $contains: [userId] }, + { $eq: null }, ], }, }], diff --git a/src/models/timeline.js b/src/models/timeline.js index d9cce184..1743c6da 100644 --- a/src/models/timeline.js +++ b/src/models/timeline.js @@ -55,11 +55,11 @@ module.exports = (sequelize, DataTypes) => { // select timelines return sequelize.query(`SELECT * FROM timelines AS timelines WHERE ${query}`, - { type: sequelize.QueryTypes.SELECT, - replacements, - logging: (str) => { log.debug(str); }, - raw: true, - }) + { type: sequelize.QueryTypes.SELECT, + replacements, + logging: (str) => { log.debug(str); }, + raw: true, + }) .then(timelines => timelines); }; diff --git a/src/permissions/constants.js b/src/permissions/constants.js index d89d8277..029b1959 100644 --- a/src/permissions/constants.js +++ b/src/permissions/constants.js @@ -45,7 +45,7 @@ * - Add a comment to such rules explaining why allow-rule cannot be created. */ import _ from 'lodash'; - import { +import { PROJECT_MEMBER_ROLE, USER_ROLE, ADMIN_ROLES as TOPCODER_ROLES_ADMINS, diff --git a/src/permissions/generalPermission.js b/src/permissions/generalPermission.js index ca7c8295..032d3706 100644 --- a/src/permissions/generalPermission.js +++ b/src/permissions/generalPermission.js @@ -31,6 +31,8 @@ import models from '../models'; /** * @param {Object|Array} permissions permission object or array of permissions + * + * @return {Function} which would be resolved if `req` is allowed and rejected otherwise */ module.exports = permissions => async (req) => { const projectId = _.parseInt(req.params.projectId); diff --git a/src/permissions/project.downloadAttachment.js b/src/permissions/project.downloadAttachment.js index 8c986d19..743bf299 100644 --- a/src/permissions/project.downloadAttachment.js +++ b/src/permissions/project.downloadAttachment.js @@ -17,21 +17,21 @@ module.exports = freq => new Promise((resolve, reject) => { return resolve(true); } return models.ProjectAttachment.getAttachmentById(projectId, attachmentId) - .then((attachment) => { - const req = freq; - req.context = req.context || {}; - req.context.existingAttachment = attachment; + .then((attachment) => { + const req = freq; + req.context = req.context || {}; + req.context.existingAttachment = attachment; - // deligate not found to the actual handler - if (!attachment) { - return resolve(true); - } + // deligate not found to the actual handler + if (!attachment) { + return resolve(true); + } - if (attachment.createdBy === userId || attachment.allowedUsers === null || + if (attachment.createdBy === userId || attachment.allowedUsers === null || attachment.allowedUsers.indexOf(userId) >= 0) { - return resolve(true); - } + return resolve(true); + } - return reject(new Error('You\'re not allowed to download')); - }); + return reject(new Error('You\'re not allowed to download')); + }); }); diff --git a/src/permissions/project.edit.js b/src/permissions/project.edit.js index a9ab5d51..29641ebf 100644 --- a/src/permissions/project.edit.js +++ b/src/permissions/project.edit.js @@ -14,19 +14,19 @@ import { MANAGER_ROLES } from '../constants'; module.exports = freq => new Promise((resolve, reject) => { const projectId = _.parseInt(freq.params.projectId); return models.ProjectMember.getActiveProjectMembers(projectId) - .then((members) => { - const req = freq; - req.context = req.context || {}; - req.context.currentProjectMembers = members; - // check if auth user has acecss to this project - const hasAccess = util.hasAdminRole(req) + .then((members) => { + const req = freq; + req.context = req.context || {}; + req.context.currentProjectMembers = members; + // check if auth user has acecss to this project + const hasAccess = util.hasAdminRole(req) || util.hasRoles(req, MANAGER_ROLES) || !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId)); - if (!hasAccess) { - // user is not an admin nor is a registered project member - return reject(new Error('You do not have permissions to perform this action')); - } - return resolve(true); - }); + if (!hasAccess) { + // user is not an admin nor is a registered project member + return reject(new Error('You do not have permissions to perform this action')); + } + return resolve(true); + }); }); diff --git a/src/permissions/project.updateAttachment.js b/src/permissions/project.updateAttachment.js index 0fe4f182..2ca57d3f 100644 --- a/src/permissions/project.updateAttachment.js +++ b/src/permissions/project.updateAttachment.js @@ -17,20 +17,20 @@ module.exports = freq => new Promise((resolve, reject) => { } return models.ProjectAttachment.getAttachmentById(projectId, attachmentId) - .then((attachment) => { - const req = freq; - req.context = req.context || {}; - req.context.existingAttachment = attachment; + .then((attachment) => { + const req = freq; + req.context = req.context || {}; + req.context.existingAttachment = attachment; - // deligate not found to the actual handler - if (!attachment) { - return resolve(true); - } + // deligate not found to the actual handler + if (!attachment) { + return resolve(true); + } - if (attachment.createdBy === req.authUser.userId) { - return resolve(true); - } + if (attachment.createdBy === req.authUser.userId) { + return resolve(true); + } - return reject(new Error('Only admins and the user that uploaded the docs can modify')); - }); + return reject(new Error('Only admins and the user that uploaded the docs can modify')); + }); }); diff --git a/src/permissions/project.view.js b/src/permissions/project.view.js index e14049ea..82bc8ea8 100644 --- a/src/permissions/project.view.js +++ b/src/permissions/project.view.js @@ -15,23 +15,23 @@ module.exports = freq => new Promise((resolve, reject) => { const projectId = _.parseInt(freq.params.projectId); const currentUserId = freq.authUser.userId; return models.ProjectMember.getActiveProjectMembers(projectId) - .then((members) => { - const req = freq; - req.context = req.context || {}; - req.context.currentProjectMembers = members; - // check if auth user has acecss to this project - const hasAccess = util.hasAdminRole(req) + .then((members) => { + const req = freq; + req.context = req.context || {}; + req.context.currentProjectMembers = members; + // check if auth user has acecss to this project + const hasAccess = util.hasAdminRole(req) || util.hasRoles(req, MANAGER_ROLES) || !_.isUndefined(_.find(members, m => m.userId === currentUserId)); - return Promise.resolve(hasAccess); - }) - .then((hasAccess) => { - if (!hasAccess) { - const errorMessage = 'You do not have permissions to perform this action'; - // user is not an admin nor is a registered project member - return reject(new Error(errorMessage)); - } - return resolve(true); - }); + return Promise.resolve(hasAccess); + }) + .then((hasAccess) => { + if (!hasAccess) { + const errorMessage = 'You do not have permissions to perform this action'; + // user is not an admin nor is a registered project member + return reject(new Error(errorMessage)); + } + return resolve(true); + }); }); diff --git a/src/routes/admin/project-index-create.js b/src/routes/admin/project-index-create.js index 109faf6d..9a6c13a4 100644 --- a/src/routes/admin/project-index-create.js +++ b/src/routes/admin/project-index-create.js @@ -19,7 +19,7 @@ const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); module.exports = [ permissions('project.admin'), - /** + /* * handles request of indexing projects */ (req, res, next) => { @@ -32,30 +32,30 @@ module.exports = [ const fields = req.query.fields; const id = req.id; return indexProjectsRange( - { - logger, - projectIdStart, - projectIdEnd, - indexName, - docType, - fields, - id, - }, - (esIndexingBody) => { - res.status(200).json({ - message: `Reindex request successfully submitted for ${ - esIndexingBody.length / 2 - } projects`, - }); - }, - ).then((result) => { - logger.debug(`project indexed successfully (projectId: ${projectIdStart}-${projectIdEnd})`, result); - logger.debug(result); - }).catch((error) => { - logger.error( - `Error in getting project details for indexing (projectId: ${projectIdStart}-${projectIdEnd})`, + { + logger, + projectIdStart, + projectIdEnd, + indexName, + docType, + fields, + id, + }, + (esIndexingBody) => { + res.status(200).json({ + message: `Reindex request successfully submitted for ${ + esIndexingBody.length / 2 + } projects`, + }); + }, + ).then((result) => { + logger.debug(`project indexed successfully (projectId: ${projectIdStart}-${projectIdEnd})`, result); + logger.debug(result); + }).catch((error) => { + logger.error( + `Error in getting project details for indexing (projectId: ${projectIdStart}-${projectIdEnd})`, error); - next(error); - }); + next(error); + }); }, ]; diff --git a/src/routes/admin/project-index-delete.js b/src/routes/admin/project-index-delete.js index 70310201..5eb0c436 100644 --- a/src/routes/admin/project-index-delete.js +++ b/src/routes/admin/project-index-delete.js @@ -23,7 +23,7 @@ const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); module.exports = [ permissions('project.admin'), - /** + /* * GET projects/{projectId} * Get a project by id */ @@ -40,7 +40,7 @@ module.exports = [ logger.debug('docType', docType); let fields = req.query.fields; fields = fields ? fields.split(',') : []; - // parse the fields string to determine what fields are to be returned + // parse the fields string to determine what fields are to be returned fields = util.parseFields(fields, { projects: PROJECT_ATTRIBUTES, project_members: PROJECT_MEMBER_ATTRIBUTES, @@ -48,37 +48,40 @@ module.exports = [ const eClient = util.getElasticSearchClient(); return models.Project.findProjectRange(models, projectIdStart, projectIdEnd, fields) - .then((_projects) => { - const projects = _projects.map((_project) => { - const project = _project; - if (!project) { - return Promise.resolve(null); - } - return Promise.resolve(project); - }); - const body = []; - Promise.all(projects).then((projectResponses) => { - projectResponses.map((p) => { - if (p) { - body.push({ delete: { _index: indexName, _type: docType, _id: p.id } }); + .then((_projects) => { + const projects = _projects.map((_project) => { + const project = _project; + if (!project) { + return Promise.resolve(null); } - // dummy return - return p; + return Promise.resolve(project); }); + const body = []; + Promise.all(projects).then((projectResponses) => { + projectResponses.map((p) => { + if (p) { + body.push({ delete: { _index: indexName, _type: docType, _id: p.id } }); + } + // dummy return + return p; + }); - // bulk delete - eClient.bulk({ - body, - }) - .then((result) => { - logger.debug(`project index deleted successfully (projectId: ${projectIdStart}-${projectIdEnd})`, result); - }) - .catch((error) => { - logger.error(`Error in deleting indexes for project (projectId: ${projectIdStart}-${projectIdEnd})`, error); + // bulk delete + eClient.bulk({ + body, + }) + .then((result) => { + logger.debug(`project index deleted successfully (projectId: ${projectIdStart}-${projectIdEnd})`, result); + }) + .catch((error) => { + logger.error( + `Error in deleting indexes for project (projectId: ${projectIdStart}-${projectIdEnd})`, + error, + ); + }); + res.status(200).json({ message: 'Delete index request successfully submitted' }); }); - res.status(200).json({ message: 'Delete index request successfully submitted' }); - }); - }) - .catch(err => next(err)); + }) + .catch(err => next(err)); }, ]; diff --git a/src/routes/attachments/create.js b/src/routes/attachments/create.js index 81525e1f..d8ee9c36 100644 --- a/src/routes/attachments/create.js +++ b/src/routes/attachments/create.js @@ -34,7 +34,7 @@ module.exports = [ // handles request validations validate(addAttachmentValidations), permissions('project.addAttachment'), - /** + /* * Add project attachment * In development mode we have to mock the ec2 file transfer and file service calls */ @@ -109,17 +109,17 @@ module.exports = [ res.status(201).json(link); return Promise.resolve(); }) - .catch((error) => { - req.log.error('Error adding link attachment', error); - const rerr = error; - rerr.status = rerr.status || 500; - next(rerr); - }); + .catch((error) => { + req.log.error('Error adding link attachment', error); + const rerr = error; + rerr.status = rerr.status || 500; + next(rerr); + }); } else { // don't actually transfer file in development mode if file uploading is disabled, so we can test this endpoint const fileTransferPromise = (process.env.NODE_ENV !== 'development' || config.get('enableFileUpload') === 'true') - ? util.s3FileTransfer(req, sourceBucket, sourceKey, destBucket, destKey) - : Promise.resolve(); + ? util.s3FileTransfer(req, sourceBucket, sourceKey, destBucket, destKey) + : Promise.resolve(); fileTransferPromise.then(() => { // file copied to final destination, create DB record @@ -164,17 +164,17 @@ module.exports = [ response.downloadUrl = resp.data.result.content.preSignedURL; // publish event req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, - newAttachment, - { correlationId: req.id }, - ); + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, + newAttachment, + { correlationId: req.id }, + ); // emit the event util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, - RESOURCES.ATTACHMENT, - newAttachment); + req, + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, + RESOURCES.ATTACHMENT, + newAttachment); res.status(201).json(response); accept(); } @@ -186,26 +186,26 @@ module.exports = [ response.downloadUrl = path; // publish event req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, - newAttachment, - { correlationId: req.id }, - ); + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, + newAttachment, + { correlationId: req.id }, + ); // emit the event util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, - RESOURCES.ATTACHMENT, - newAttachment); + req, + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED, + RESOURCES.ATTACHMENT, + newAttachment); res.status(201).json(response); return Promise.resolve(); }) - .catch((error) => { - req.log.error('Error adding file attachment', error); - const rerr = error; - rerr.status = rerr.status || 500; - next(rerr); - }); + .catch((error) => { + req.log.error('Error adding file attachment', error); + const rerr = error; + rerr.status = rerr.status || 500; + next(rerr); + }); } }, ]; diff --git a/src/routes/attachments/create.spec.js b/src/routes/attachments/create.spec.js index d3bcf819..ad9df03f 100644 --- a/src/routes/attachments/create.spec.js +++ b/src/routes/attachments/create.spec.js @@ -73,38 +73,38 @@ describe('Project Attachments', () => { // mocks testUtil.clearDb() - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + models.ProjectMember.create({ + userId: 40051332, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - models.ProjectMember.create({ - userId: 40051332, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }).then(() => { - sandbox = sinon.sandbox.create(); - postSpy = sandbox.spy(mockHttpClient, 'post'); - getSpy = sandbox.spy(mockHttpClient, 'get'); - stub = sandbox.stub(util, 'getHttpClient', () => mockHttpClient); - sandbox.stub(util, 's3FileTransfer').returns(Promise.resolve(true)); - done(); - }); + }).then(() => { + sandbox = sinon.sandbox.create(); + postSpy = sandbox.spy(mockHttpClient, 'post'); + getSpy = sandbox.spy(mockHttpClient, 'get'); + stub = sandbox.stub(util, 'getHttpClient', () => mockHttpClient); + sandbox.stub(util, 's3FileTransfer').returns(Promise.resolve(true)); + done(); }); }); + }); }); afterEach((done) => { @@ -115,94 +115,94 @@ describe('Project Attachments', () => { describe('POST /projects/{id}/attachments/', () => { it('should return 403 if user does not have permissions', (done) => { request(server) - .post(`/v5/projects/${project1.id}/attachments/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send(fileAttachmentBody) - .expect('Content-Type', /json/) - .expect(403, done); + .post(`/v5/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send(fileAttachmentBody) + .expect('Content-Type', /json/) + .expect(403, done); }); it('should return 400 if contentType is not provided for file attachment', (done) => { const payload = _.omit(_.cloneDeep(fileAttachmentBody), 'contentType'); request(server) - .post(`/v5/projects/${project1.id}/attachments/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(payload) - .expect('Content-Type', /json/) - .expect(400, done); + .post(`/v5/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(payload) + .expect('Content-Type', /json/) + .expect(400, done); }); it('should return 400 if s3Bucket is not provided for file attachment', (done) => { const payload = _.omit(_.cloneDeep(fileAttachmentBody), 's3Bucket'); request(server) - .post(`/v5/projects/${project1.id}/attachments/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(payload) - .expect('Content-Type', /json/) - .expect(400, done); + .post(`/v5/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(payload) + .expect('Content-Type', /json/) + .expect(400, done); }); it('should properly create file attachment - 201', (done) => { request(server) - .post(`/v5/projects/${project1.id}/attachments/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(fileAttachmentBody) - .expect('Content-Type', /json/) - .expect(201) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - postSpy.should.have.been.calledOnce; - getSpy.should.have.been.calledOnce; - stub.restore(); - resJson.title.should.equal(fileAttachmentBody.title); - resJson.tags.should.eql(fileAttachmentBody.tags); - resJson.type.should.eql(fileAttachmentBody.type); - resJson.downloadUrl.should.exist; - resJson.projectId.should.equal(project1.id); - done(); - } - }); + .post(`/v5/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(fileAttachmentBody) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + postSpy.should.have.been.calledOnce; + getSpy.should.have.been.calledOnce; + stub.restore(); + resJson.title.should.equal(fileAttachmentBody.title); + resJson.tags.should.eql(fileAttachmentBody.tags); + resJson.type.should.eql(fileAttachmentBody.type); + resJson.downloadUrl.should.exist; + resJson.projectId.should.equal(project1.id); + done(); + } + }); }); it('should properly create link attachment - 201', (done) => { request(server) - .post(`/v5/projects/${project1.id}/attachments/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(linkAttachmentBody) - .expect('Content-Type', /json/) - .expect(201) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - postSpy.should.have.been.calledOnce; - getSpy.should.have.been.calledOnce; - stub.restore(); - resJson.title.should.equal(linkAttachmentBody.title); - resJson.path.should.equal(linkAttachmentBody.path); - resJson.description.should.equal(linkAttachmentBody.description); - resJson.type.should.equal(linkAttachmentBody.type); - resJson.tags.should.eql(linkAttachmentBody.tags); - resJson.projectId.should.equal(project1.id); - done(); - } - }); + .post(`/v5/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(linkAttachmentBody) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + postSpy.should.have.been.calledOnce; + getSpy.should.have.been.calledOnce; + stub.restore(); + resJson.title.should.equal(linkAttachmentBody.title); + resJson.path.should.equal(linkAttachmentBody.path); + resJson.description.should.equal(linkAttachmentBody.description); + resJson.type.should.equal(linkAttachmentBody.type); + resJson.tags.should.eql(linkAttachmentBody.tags); + resJson.projectId.should.equal(project1.id); + done(); + } + }); }); describe('Bus api', () => { diff --git a/src/routes/attachments/delete.js b/src/routes/attachments/delete.js index 9d961b01..723e32e9 100644 --- a/src/routes/attachments/delete.js +++ b/src/routes/attachments/delete.js @@ -26,12 +26,12 @@ module.exports = [ let attachment; models.sequelize.transaction(() => // soft delete the record - models.ProjectAttachment.findOne({ - where: { - id: attachmentId, - projectId, - }, - }) + models.ProjectAttachment.findOne({ + where: { + id: attachmentId, + projectId, + }, + }) .then((_attachment) => { if (!_attachment) { const err = new Error('Record not found'); @@ -42,29 +42,29 @@ module.exports = [ return _attachment.update({ deletedBy: req.authUser.userId }) .then(() => _attachment.destroy()); })) - .then((_attachment) => { - if (_attachment.type === ATTACHMENT_TYPES.FILE && + .then((_attachment) => { + if (_attachment.type === ATTACHMENT_TYPES.FILE && (process.env.NODE_ENV !== 'development' || config.get('enableFileUpload') === 'true')) { - return fileService.deleteFile(req, _attachment.path); - } - return Promise.resolve(); - }) - .then(() => { - // fire event - const pattachment = attachment.get({ plain: true }); - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED, - pattachment, - { correlationId: req.id }, - ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED, - RESOURCES.ATTACHMENT, - pattachment); - res.status(204).json({}); - }) - .catch(err => next(err)); + return fileService.deleteFile(req, _attachment.path); + } + return Promise.resolve(); + }) + .then(() => { + // fire event + const pattachment = attachment.get({ plain: true }); + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED, + pattachment, + { correlationId: req.id }, + ); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED, + RESOURCES.ATTACHMENT, + pattachment); + res.status(204).json({}); + }) + .catch(err => next(err)); }, ]; diff --git a/src/routes/attachments/delete.spec.js b/src/routes/attachments/delete.spec.js index 680e1ea9..b308b1a5 100644 --- a/src/routes/attachments/delete.spec.js +++ b/src/routes/attachments/delete.spec.js @@ -20,30 +20,30 @@ describe('Project Attachments delete', () => { attachments = []; testUtil.clearDb() .then(() => testUtil.clearES()) - .then(() => { - models.Project.create({ - type: 'generic', - directProjectId: 1, - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .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: 40051332, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - return models.ProjectMember.create({ - userId: 40051332, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }).then(() => + }).then(() => models.ProjectAttachment.create({ projectId: project1.id, title: 'file1.txt', @@ -71,12 +71,12 @@ describe('Project Attachments delete', () => { createdBy: testUtil.userIds.copilot, updatedBy: 1, }).then((link) => { - attachments.push(link); - done(); - }); + attachments.push(link); + done(); + }); })); - }); }); + }); }); after((done) => { @@ -94,22 +94,22 @@ describe('Project Attachments delete', () => { it('should return 403 if user does not have permissions', (done) => { request(server) - .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send({ userId: 1, projectId: project1.id, role: 'customer' }) - .expect(403, done); + .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send({ userId: 1, projectId: project1.id, role: 'customer' }) + .expect(403, done); }); it('should return 404 if attachment was not found', (done) => { request(server) - .delete(`/v5/projects/${project1.id}/attachments/8888888`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ userId: 1, projectId: project1.id, role: 'customer' }) - .expect(404, done); + .delete(`/v5/projects/${project1.id}/attachments/8888888`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ userId: 1, projectId: project1.id, role: 'customer' }) + .expect(404, done); }); @@ -131,64 +131,64 @@ describe('Project Attachments delete', () => { const deleteSpy = sinon.spy(mockHttpClient, 'delete'); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - setTimeout(() => - models.ProjectAttachment.findOne({ - where: { - projectId: project1.id, - id: attachments[0].id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - deleteSpy.calledOnce.should.be.true; + .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + setTimeout(() => + models.ProjectAttachment.findOne({ + where: { + projectId: project1.id, + id: attachments[0].id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + deleteSpy.calledOnce.should.be.true; - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); - } - }), 500); - } - }); + request(server) + .get(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, done); + } + }), 500); + } + }); }); it('should return 204 if ADMIN deletes the file attachment successfully', (done) => { request(server) - .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ userId: 1, projectId: project1.id, role: 'customer' }) - .expect(204, done) - .end((err) => { - if (err) { - done(err); - } else { - request(server) + .delete(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ userId: 1, projectId: project1.id, role: 'customer' }) + .expect(204, done) + .end((err) => { + if (err) { + done(err); + } else { + request(server) .get(`/v5/projects/${project1.id}/attachments/${attachments[0].id}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect(404, done); - } - }); + } + }); }); it('should return 204 if the CREATOR removes the link attachment successfully', (done) => { @@ -209,63 +209,63 @@ describe('Project Attachments delete', () => { const deleteSpy = sinon.spy(mockHttpClient, 'delete'); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .delete(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - setTimeout(() => - models.ProjectAttachment.findOne({ - where: { - projectId: project1.id, - id: attachments[1].id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - deleteSpy.called.should.be.false; - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + .delete(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + setTimeout(() => + models.ProjectAttachment.findOne({ + where: { + projectId: project1.id, + id: attachments[1].id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + deleteSpy.called.should.be.false; + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); - } - }), 500); - } - }); + request(server) + .get(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, done); + } + }), 500); + } + }); }); it('should return 204 if ADMIN deletes the link attachment successfully', (done) => { request(server) - .delete(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ userId: 1, projectId: project1.id, role: 'customer' }) - .expect(204, done) - .end((err) => { - if (err) { - done(err); - } else { - request(server) + .delete(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ userId: 1, projectId: project1.id, role: 'customer' }) + .expect(204, done) + .end((err) => { + if (err) { + done(err); + } else { + request(server) .get(`/v5/projects/${project1.id}/attachments/${attachments[1].id}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect(404, done); - } - }); + } + }); }); describe('Bus api', () => { diff --git a/src/routes/attachments/get.js b/src/routes/attachments/get.js index bd5081f6..20f14805 100644 --- a/src/routes/attachments/get.js +++ b/src/routes/attachments/get.js @@ -62,44 +62,44 @@ module.exports = [ }, }, }) - .then((data) => { - if (data.length === 0) { - req.log.debug('No attachment found in ES'); - return models.ProjectAttachment.findOne( - { - where: { - id: attachmentId, - projectId, - }, - }) - .then((attachment) => { - if (!attachment) { - const err = new Error('Record not found'); - err.status = 404; - return Promise.reject(err); - } - return getPreSignedUrl(req, attachment); - }) - .catch((error) => { - req.log.error('Error fetching attachment', error); - const rerr = error; - rerr.status = rerr.status || 500; - next(rerr); - }); - } - req.log.debug('attachment found in ES'); - const attachment = data[0].inner_hits.attachments.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle + .then((data) => { + if (data.length === 0) { + req.log.debug('No attachment found in ES'); + return models.ProjectAttachment.findOne( + { + where: { + id: attachmentId, + projectId, + }, + }) + .then((attachment) => { + if (!attachment) { + const err = new Error('Record not found'); + err.status = 404; + return Promise.reject(err); + } + return getPreSignedUrl(req, attachment); + }) + .catch((error) => { + req.log.error('Error fetching attachment', error); + const rerr = error; + rerr.status = rerr.status || 500; + next(rerr); + }); + } + req.log.debug('attachment found in ES'); + const attachment = data[0].inner_hits.attachments.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle - return getPreSignedUrl(req, attachment); - }) - .then((result) => { - req.log.debug('getPresigned url result: ', JSON.stringify(result)); - if (_.isEmpty(result[1])) { - return res.json(result[0]); - } + return getPreSignedUrl(req, attachment); + }) + .then((result) => { + req.log.debug('getPresigned url result: ', JSON.stringify(result)); + if (_.isEmpty(result[1])) { + return res.json(result[0]); + } - return res.json(_.extend(result[0], { url: result[1] })); - }) - .catch(next); + return res.json(_.extend(result[0], { url: result[1] })); + }) + .catch(next); }, ]; diff --git a/src/routes/attachments/get.spec.js b/src/routes/attachments/get.spec.js index 4b8f6f5b..efa831ed 100644 --- a/src/routes/attachments/get.spec.js +++ b/src/routes/attachments/get.spec.js @@ -21,48 +21,48 @@ describe('Get Project attachments Tests', () => { 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: {}, + .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: 40051332, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - return models.ProjectMember.create({ - userId: 40051332, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }).then(() => models.ProjectAttachment.create({ - projectId: project1.id, - title: 'test.txt', - description: 'blah', - contentType: 'application/unknown', - size: 12312, - category: null, - path: 'https://media.topcoder.com/projects/1/test.txt', - type: ATTACHMENT_TYPES.FILE, - tags: ['tag1', 'tag2'], - createdBy: testUtil.userIds.copilot, - updatedBy: 1, - allowedUsers: [testUtil.userIds.member], - }).then((a1) => { - attachment = a1; - done(); - })); - }); + }).then(() => models.ProjectAttachment.create({ + projectId: project1.id, + title: 'test.txt', + description: 'blah', + contentType: 'application/unknown', + size: 12312, + category: null, + path: 'https://media.topcoder.com/projects/1/test.txt', + type: ATTACHMENT_TYPES.FILE, + tags: ['tag1', 'tag2'], + createdBy: testUtil.userIds.copilot, + updatedBy: 1, + allowedUsers: [testUtil.userIds.member], + }).then((a1) => { + attachment = a1; + done(); + })); }); + }); }); after((done) => { diff --git a/src/routes/attachments/list.js b/src/routes/attachments/list.js index d77c7513..19b5fd75 100644 --- a/src/routes/attachments/list.js +++ b/src/routes/attachments/list.js @@ -44,22 +44,22 @@ module.exports = [ }, }, }) - .then((data) => { - if (data.length === 0) { - req.log.debug('No attachment found in ES'); - return models.ProjectAttachment.findAll({ - where: { - projectId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then(attachments => res.json(attachments)) - .catch(next); - } - req.log.debug('attachments found in ES'); - return res.json(data[0].inner_hits.attachments.hits.hits.map(hit => hit._source)); // eslint-disable-line no-underscore-dangle - }) - .catch(next); + .then((data) => { + if (data.length === 0) { + req.log.debug('No attachment found in ES'); + return models.ProjectAttachment.findAll({ + where: { + projectId, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then(attachments => res.json(attachments)) + .catch(next); + } + req.log.debug('attachments found in ES'); + return res.json(data[0].inner_hits.attachments.hits.hits.map(hit => hit._source)); // eslint-disable-line no-underscore-dangle + }) + .catch(next); }, ]; diff --git a/src/routes/attachments/list.spec.js b/src/routes/attachments/list.spec.js index 52203c7a..433243ea 100644 --- a/src/routes/attachments/list.spec.js +++ b/src/routes/attachments/list.spec.js @@ -15,55 +15,55 @@ describe('Project Attachments download', () => { 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: {}, + .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: 40051332, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - return models.ProjectMember.create({ - userId: 40051332, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }).then(() => models.ProjectAttachment.bulkCreate([{ - projectId: project1.id, - title: 'test.txt', - description: 'blah', - contentType: 'application/unknown', - size: 12312, - category: null, - path: 'https://media.topcoder.com/projects/1/test.txt', - type: ATTACHMENT_TYPES.FILE, - tags: ['tag1', 'tag2'], - createdBy: testUtil.userIds.copilot, - updatedBy: 1, - }, { - projectId: project1.id, - title: 'link test 1', - description: 'link test description', - size: 123456, - category: null, - path: 'https://media.topcoder.com/projects/1/test2.txt', - type: ATTACHMENT_TYPES.LINK, - tags: ['tag3'], - createdBy: testUtil.userIds.copilot, - updatedBy: 1, - }]).then(() => done())); - }); + }).then(() => models.ProjectAttachment.bulkCreate([{ + projectId: project1.id, + title: 'test.txt', + description: 'blah', + contentType: 'application/unknown', + size: 12312, + category: null, + path: 'https://media.topcoder.com/projects/1/test.txt', + type: ATTACHMENT_TYPES.FILE, + tags: ['tag1', 'tag2'], + createdBy: testUtil.userIds.copilot, + updatedBy: 1, + }, { + projectId: project1.id, + title: 'link test 1', + description: 'link test description', + size: 123456, + category: null, + path: 'https://media.topcoder.com/projects/1/test2.txt', + type: ATTACHMENT_TYPES.LINK, + tags: ['tag3'], + createdBy: testUtil.userIds.copilot, + updatedBy: 1, + }]).then(() => done())); }); + }); }); after((done) => { diff --git a/src/routes/attachments/update.js b/src/routes/attachments/update.js index b52b9960..a8beb68d 100644 --- a/src/routes/attachments/update.js +++ b/src/routes/attachments/update.js @@ -28,7 +28,7 @@ module.exports = [ // handles request validations validate(updateProjectAttachmentValidation), permissions('project.updateAttachment'), - /** + /* * Update a attachment if the user has access */ (req, res, next) => { @@ -44,7 +44,7 @@ module.exports = [ }, }).then(existing => new Promise((accept, reject) => { if (!existing) { - // handle 404 + // handle 404 const err = new Error('project attachment not found for project id ' + `${projectId} and member id ${attachmentId}`); err.status = 404; diff --git a/src/routes/attachments/update.spec.js b/src/routes/attachments/update.spec.js index 80050d79..9331747a 100644 --- a/src/routes/attachments/update.spec.js +++ b/src/routes/attachments/update.spec.js @@ -17,63 +17,63 @@ describe('Project Attachments update', () => { let link; beforeEach((done) => { testUtil.clearDb() - .then(() => { - models.Project.create({ - type: 'generic', - directProjectId: 1, - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .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: 40051332, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - return models.ProjectMember.create({ - userId: 40051332, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }).then(() => models.ProjectAttachment.create({ - projectId: project1.id, - title: 'test.txt', - description: 'blah', - contentType: 'application/unknown', - size: 12312, - category: null, - path: 'https://media.topcoder.com/projects/1/test.txt', - type: ATTACHMENT_TYPES.FILE, - tags: ['tag1', 'tag2', 'tag3'], - createdBy: testUtil.userIds.copilot, - updatedBy: 1, - allowedUsers: [], - }).then((a1) => { - attachment = a1; - models.ProjectAttachment.create( - { - projectId: project1.id, - title: 'Test Link 1', - description: 'Test link 1 description', - size: 123456, - category: null, - path: 'https://connect.topcoder-dev.com/projects/8600/assets', - type: ATTACHMENT_TYPES.LINK, - tags: ['tag3', 'tag4'], - createdBy: testUtil.userIds.copilot, - updatedBy: 1, - }).then((_link) => { - link = _link; - done(); - }); - })); - }); + }).then(() => models.ProjectAttachment.create({ + projectId: project1.id, + title: 'test.txt', + description: 'blah', + contentType: 'application/unknown', + size: 12312, + category: null, + path: 'https://media.topcoder.com/projects/1/test.txt', + type: ATTACHMENT_TYPES.FILE, + tags: ['tag1', 'tag2', 'tag3'], + createdBy: testUtil.userIds.copilot, + updatedBy: 1, + allowedUsers: [], + }).then((a1) => { + attachment = a1; + models.ProjectAttachment.create( + { + projectId: project1.id, + title: 'Test Link 1', + description: 'Test link 1 description', + size: 123456, + category: null, + path: 'https://connect.topcoder-dev.com/projects/8600/assets', + type: ATTACHMENT_TYPES.LINK, + tags: ['tag3', 'tag4'], + createdBy: testUtil.userIds.copilot, + updatedBy: 1, + }).then((_link) => { + link = _link; + done(); + }); + })); }); + }); }); after((done) => { diff --git a/src/routes/form/revision/create.js b/src/routes/form/revision/create.js index 99c814f2..9642417d 100644 --- a/src/routes/form/revision/create.js +++ b/src/routes/form/revision/create.js @@ -60,7 +60,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.FORM_REVISION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/form/revision/create.spec.js b/src/routes/form/revision/create.spec.js index 3cfcf3c3..8c2cfdd6 100644 --- a/src/routes/form/revision/create.spec.js +++ b/src/routes/form/revision/create.spec.js @@ -60,10 +60,10 @@ describe('CREATE Form Revision', () => { it('should return 404 if missing key', (done) => { request(server) - .post('/v5/projects/metadata/form/no-exist-key/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/form/no-exist-key/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -71,10 +71,10 @@ describe('CREATE Form Revision', () => { it('should return 404 if missing version', (done) => { request(server) - .post('/v5/projects/metadata/form/dev/versions/100/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/form/dev/versions/100/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -97,10 +97,10 @@ describe('CREATE Form Revision', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/form/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/form/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -123,7 +123,7 @@ describe('CREATE Form Revision', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/form/dev/versions/1/revisions') + .post('/v5/projects/metadata/form/dev/versions/1/revisions') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) diff --git a/src/routes/form/revision/delete.js b/src/routes/form/revision/delete.js index e06b9f80..0c62e9d7 100644 --- a/src/routes/form/revision/delete.js +++ b/src/routes/form/revision/delete.js @@ -32,21 +32,21 @@ module.exports = [ revision: req.params.revision, }, }).then((form) => { - if (!form) { - const apiErr = new Error('Form not found for key' + + if (!form) { + const apiErr = new Error('Form not found for key' + ` ${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return form.update({ - deletedBy: req.authUser.userId, - }); - }).then(form => form.destroy()) + apiErr.status = 404; + return Promise.reject(apiErr); + } + return form.update({ + deletedBy: req.authUser.userId, + }); + }).then(form => form.destroy()) .then((form) => { util.sendResourceToKafkaBus(req, EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.FORM_REVISION, - _.pick(form.toJSON(), 'id')); + RESOURCES.FORM_REVISION, + _.pick(form.toJSON(), 'id')); res.status(204).end(); }) .catch(next)); diff --git a/src/routes/form/revision/delete.spec.js b/src/routes/form/revision/delete.spec.js index 8aee319f..9e35fd77 100644 --- a/src/routes/form/revision/delete.spec.js +++ b/src/routes/form/revision/delete.spec.js @@ -10,29 +10,29 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.Form.findOne({ - where: { - key: 'dev', - version: 1, - revision: 1, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.Form.findOne({ + where: { + key: 'dev', + version: 1, + revision: 1, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; @@ -78,34 +78,34 @@ describe('DELETE form revision', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403, done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403, done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403, done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/form/no-existed-key/versions/1/revisions/1') + .delete('/v5/projects/metadata/form/no-existed-key/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -114,7 +114,7 @@ describe('DELETE form revision', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/100/revisions/1') + .delete('/v5/projects/metadata/form/dev/versions/100/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -124,7 +124,7 @@ describe('DELETE form revision', () => { it('should return 404 for non-existed revision', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/100') + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/100') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -133,17 +133,17 @@ describe('DELETE form revision', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') + .delete('/v5/projects/metadata/form/dev/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/form/revision/get.js b/src/routes/form/revision/get.js index cc0ec5d5..b6784c61 100644 --- a/src/routes/form/revision/get.js +++ b/src/routes/form/revision/get.js @@ -43,35 +43,35 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No form found in ES'); - models.Form.findOne({ - where: { - key: req.params.key, - version: req.params.version, - revision: req.params.revision, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((form) => { - // Not found - if (!form) { - const apiErr = new Error('Form not found for key' + + .then((data) => { + if (data.length === 0) { + req.log.debug('No form found in ES'); + models.Form.findOne({ + where: { + key: req.params.key, + version: req.params.version, + revision: req.params.revision, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((form) => { + // Not found + if (!form) { + const apiErr = new Error('Form not found for key' + ` ${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(form); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('forms found in ES'); - res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(form); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('forms found in ES'); + res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/form/revision/get.spec.js b/src/routes/form/revision/get.spec.js index bdcdff23..1f4bf2d6 100644 --- a/src/routes/form/revision/get.spec.js +++ b/src/routes/form/revision/get.spec.js @@ -70,45 +70,45 @@ describe('GET a particular revision of specific version Form', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') - .expect(403, done); + .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/form/revision/list.js b/src/routes/form/revision/list.js index ab0ab9f9..68bc71a8 100644 --- a/src/routes/form/revision/list.js +++ b/src/routes/form/revision/list.js @@ -21,30 +21,30 @@ module.exports = [ permissions('form.view'), (req, res, next) => util.fetchFromES('forms') - .then((data) => { - if (data.forms.length === 0) { - req.log.debug('No form found in ES'); - models.Form.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }).then((forms) => { - // Not found - if ((!forms) || (forms.length === 0)) { - const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + .then((data) => { + if (data.forms.length === 0) { + req.log.debug('No form found in ES'); + models.Form.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }).then((forms) => { + // Not found + if ((!forms) || (forms.length === 0)) { + const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(forms); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('forms found in ES'); - res.json(data.forms); - } - }).catch(next), + res.json(forms); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('forms found in ES'); + res.json(data.forms); + } + }).catch(next), ]; diff --git a/src/routes/form/revision/list.spec.js b/src/routes/form/revision/list.spec.js index d239b03b..3a07ec39 100644 --- a/src/routes/form/revision/list.spec.js +++ b/src/routes/form/revision/list.spec.js @@ -72,45 +72,45 @@ describe('LIST form revisions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions') - .expect(403, done); + .get('/v5/projects/metadata/form/dev/versions/1/revisions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/form/version/create.js b/src/routes/form/version/create.js index 00a16119..3d1e2565 100644 --- a/src/routes/form/version/create.js +++ b/src/routes/form/version/create.js @@ -60,7 +60,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.FORM_VERSION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/form/version/create.spec.js b/src/routes/form/version/create.spec.js index f94eb023..acd1a11c 100644 --- a/src/routes/form/version/create.spec.js +++ b/src/routes/form/version/create.spec.js @@ -73,10 +73,10 @@ describe('CREATE Form version', () => { }); request(server) - .post('/v5/projects/metadata/form/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/form/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400, done); @@ -84,10 +84,10 @@ describe('CREATE Form version', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/form/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/form/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -110,10 +110,10 @@ describe('CREATE Form version', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/form/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .post('/v5/projects/metadata/form/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403, done); }); diff --git a/src/routes/form/version/delete.js b/src/routes/form/version/delete.js index 05359a37..36e71e99 100644 --- a/src/routes/form/version/delete.js +++ b/src/routes/form/version/delete.js @@ -29,42 +29,42 @@ module.exports = [ version: req.params.version, }, }).then((allRevision) => { - if (allRevision.length === 0) { - const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return models.Form.update( - { - deletedBy: req.authUser.userId, - }, { - where: { - key: req.params.key, - version: req.params.version, - }, - }); - }) - .then(() => models.Form.destroy({ - where: { - key: req.params.key, - version: req.params.version, - }, - })).then(deleted => models.Form.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - paranoid: false, - order: [['deletedAt', 'DESC']], - limit: deleted, - })) - .then((forms) => { - _.map(forms, form => util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.FORM_VERSION, - _.pick(form.toJSON(), 'id'))); - res.status(204).end(); + if (allRevision.length === 0) { + const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } + return models.Form.update( + { + deletedBy: req.authUser.userId, + }, { + where: { + key: req.params.key, + version: req.params.version, + }, + }); }) - .catch(next)); + .then(() => models.Form.destroy({ + where: { + key: req.params.key, + version: req.params.version, + }, + })).then(deleted => models.Form.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + paranoid: false, + order: [['deletedAt', 'DESC']], + limit: deleted, + })) + .then((forms) => { + _.map(forms, form => util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.FORM_VERSION, + _.pick(form.toJSON(), 'id'))); + res.status(204).end(); + }) + .catch(next)); }, ]; diff --git a/src/routes/form/version/delete.spec.js b/src/routes/form/version/delete.spec.js index 8aff6a9b..b3887d5f 100644 --- a/src/routes/form/version/delete.spec.js +++ b/src/routes/form/version/delete.spec.js @@ -10,25 +10,25 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.Form.findAll({ - where: { - key: 'dev', - version: 1, - }, - paranoid: false, - }) - .then((forms) => { - if (forms.length === 0) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(forms[0].deletedAt); - chai.assert.isNotNull(forms[0].deletedBy); + models.Form.findAll({ + where: { + key: 'dev', + version: 1, + }, + paranoid: false, + }) + .then((forms) => { + if (forms.length === 0) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(forms[0].deletedAt); + chai.assert.isNotNull(forms[0].deletedBy); - chai.assert.isNotNull(forms[1].deletedAt); - chai.assert.isNotNull(forms[1].deletedBy); - next(); - } - }), 500); + chai.assert.isNotNull(forms[1].deletedAt); + chai.assert.isNotNull(forms[1].deletedBy); + next(); + } + }), 500); }; @@ -75,34 +75,34 @@ describe('DELETE form version', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403, done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403, done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403, done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev111/versions/1') + .delete('/v5/projects/metadata/form/dev111/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -111,7 +111,7 @@ describe('DELETE form version', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/111') + .delete('/v5/projects/metadata/form/dev/versions/111') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -120,17 +120,17 @@ describe('DELETE form version', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/form/dev/versions/1') + .delete('/v5/projects/metadata/form/dev/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/form/version/get.js b/src/routes/form/version/get.js index 0779b9f5..6719770b 100644 --- a/src/routes/form/version/get.js +++ b/src/routes/form/version/get.js @@ -41,25 +41,25 @@ module.exports = [ }, sort: { 'forms.version': 'desc' }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No form found in ES'); - models.Form.latestRevisionOfLatestVersion(req.params.key) - .then((form) => { - if (form == null) { - const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(form); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('forms found in ES'); - res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + .then((data) => { + if (data.length === 0) { + req.log.debug('No form found in ES'); + models.Form.latestRevisionOfLatestVersion(req.params.key) + .then((form) => { + if (form == null) { + const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(form); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('forms found in ES'); + res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/form/version/get.spec.js b/src/routes/form/version/get.spec.js index 326d4ef7..38dcef08 100644 --- a/src/routes/form/version/get.spec.js +++ b/src/routes/form/version/get.spec.js @@ -90,45 +90,45 @@ describe('GET a latest version of specific key of Form', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .expect(403, done); + .get('/v5/projects/metadata/form/dev') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/form/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/form/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/form/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/form/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/form/version/getVersion.js b/src/routes/form/version/getVersion.js index d4167150..92abaf0a 100644 --- a/src/routes/form/version/getVersion.js +++ b/src/routes/form/version/getVersion.js @@ -43,26 +43,26 @@ module.exports = [ }, sort: { 'forms.revision': 'desc' }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No form found in ES'); - return models.Form.findOneWithLatestRevision(req.params) - .then((form) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No form found in ES'); + return models.Form.findOneWithLatestRevision(req.params) + .then((form) => { // Not found - if (!form) { - const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(form); - return Promise.resolve(); - }) - .catch(next); - } - req.log.debug('forms found in ES'); - res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - return Promise.resolve(); - }) - .catch(next); + if (!form) { + const apiErr = new Error(`Form not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(form); + return Promise.resolve(); + }) + .catch(next); + } + req.log.debug('forms found in ES'); + res.json(data[0].inner_hits.forms.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/form/version/getVersion.spec.js b/src/routes/form/version/getVersion.spec.js index 14ac9096..862d6fa5 100644 --- a/src/routes/form/version/getVersion.spec.js +++ b/src/routes/form/version/getVersion.spec.js @@ -81,45 +81,45 @@ describe('GET a particular version of specific key of Form', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1') - .expect(403, done); + .get('/v5/projects/metadata/form/dev/versions/1') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/form/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/form/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/form/version/list.js b/src/routes/form/version/list.js index 383cec6f..8ae7e39b 100644 --- a/src/routes/form/version/list.js +++ b/src/routes/form/version/list.js @@ -19,39 +19,39 @@ module.exports = [ validate(schema), permissions('form.view'), (req, res, next) => - util.fetchFromES('forms') - .then((data) => { - if (data.forms.length === 0) { - req.log.debug('No form found in ES'); - models.Form.findAll({ - where: { - key: req.params.key, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((forms) => { - // Not found - if ((!forms) || (forms.length === 0)) { - const apiErr = new Error(`Form not found for key ${req.params.key}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + util.fetchFromES('forms') + .then((data) => { + if (data.forms.length === 0) { + req.log.debug('No form found in ES'); + models.Form.findAll({ + where: { + key: req.params.key, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((forms) => { + // Not found + if ((!forms) || (forms.length === 0)) { + const apiErr = new Error(`Form not found for key ${req.params.key}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - const latestForms = {}; - forms.forEach((element) => { - const isNewerRevision = (latestForms[element.version] != null) && + const latestForms = {}; + forms.forEach((element) => { + const isNewerRevision = (latestForms[element.version] != null) && (latestForms[element.version].revision < element.revision); - if ((latestForms[element.version] == null) || isNewerRevision) { - latestForms[element.version] = element; - } - }); - res.json(Object.values(latestForms)); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('forms found in ES'); - res.json(data.forms); - } - }).catch(next), + if ((latestForms[element.version] == null) || isNewerRevision) { + latestForms[element.version] = element; + } + }); + res.json(Object.values(latestForms)); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('forms found in ES'); + res.json(data.forms); + } + }).catch(next), ]; diff --git a/src/routes/form/version/list.spec.js b/src/routes/form/version/list.spec.js index 14977b5e..03616456 100644 --- a/src/routes/form/version/list.spec.js +++ b/src/routes/form/version/list.spec.js @@ -72,45 +72,45 @@ describe('LIST form versions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions') - .expect(403, done); + .get('/v5/projects/metadata/form/dev/versions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/form/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/form/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/form/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/form/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/form/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/form/version/update.js b/src/routes/form/version/update.js index b484f830..6d690a66 100644 --- a/src/routes/form/version/update.js +++ b/src/routes/form/version/update.js @@ -53,26 +53,26 @@ module.exports = [ } return Promise.resolve(forms[0]); }) - .then((form) => { - const revision = form.revision + 1; - const entity = { - version: req.params.version, - revision, - createdBy: req.authUser.userId, - updatedBy: req.authUser.userId, - key: req.params.key, - config: req.body.config, - }; - return models.Form.create(entity); - }) - .then((createdEntity) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, - RESOURCES.FORM_VERSION, - createdEntity.toJSON()); - // Omit deletedAt, deletedBy - res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); - }) - .catch(next)); + .then((form) => { + const revision = form.revision + 1; + const entity = { + version: req.params.version, + revision, + createdBy: req.authUser.userId, + updatedBy: req.authUser.userId, + key: req.params.key, + config: req.body.config, + }; + return models.Form.create(entity); + }) + .then((createdEntity) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, + RESOURCES.FORM_VERSION, + createdEntity.toJSON()); + // Omit deletedAt, deletedBy + res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); + }) + .catch(next)); }, ]; diff --git a/src/routes/form/version/update.spec.js b/src/routes/form/version/update.spec.js index 0bce1fd7..0f728dd0 100644 --- a/src/routes/form/version/update.spec.js +++ b/src/routes/form/version/update.spec.js @@ -63,10 +63,10 @@ describe('UPDATE Form version', () => { config: undefined, }); request(server) - .patch('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400, done); @@ -74,10 +74,10 @@ describe('UPDATE Form version', () => { it('should return 201 for admin', (done) => { request(server) - .patch('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -100,10 +100,10 @@ describe('UPDATE Form version', () => { it('should return 403 for member', (done) => { request(server) - .patch('/v5/projects/metadata/form/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .patch('/v5/projects/metadata/form/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403, done); }); diff --git a/src/routes/index.js b/src/routes/index.js index 96427c0f..801da331 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -115,11 +115,11 @@ router.route('/v5/projects/:projectId(\\d+)') router.route('/v5/projects/:projectId(\\d+)/scopeChangeRequests') .post(require('./scopeChangeRequests/create')); - // .get(require('./scopeChangeRequests/list')); +// .get(require('./scopeChangeRequests/list')); router.route('/v5/projects/:projectId(\\d+)/scopeChangeRequests/:requestId(\\d+)') // .get(require('./scopeChangeRequests/get')) .patch(require('./scopeChangeRequests/update')); - // .delete(require('./scopeChangeRequests/delete')); +// .delete(require('./scopeChangeRequests/delete')); router.route('/v5/projects/:projectId(\\d+)/members') .get(require('./projectMembers/list')) @@ -278,16 +278,16 @@ router.route('/v5/projects/metadata/priceConfig/:key/versions/:version(\\d+)/rev .post(require('./priceConfig/revision/create')); router.route('/v5/projects/metadata/priceConfig/:key') -.get(require('./priceConfig/version/get')); + .get(require('./priceConfig/version/get')); router.route('/v5/projects/metadata/priceConfig/:key/versions') -.get(require('./priceConfig/version/list')) -.post(require('./priceConfig/version/create')); + .get(require('./priceConfig/version/list')) + .post(require('./priceConfig/version/create')); router.route('/v5/projects/metadata/priceConfig/:key/versions/:version(\\d+)') -.get(require('./priceConfig/version/getVersion')) -.patch(require('./priceConfig/version/update')) -.delete(require('./priceConfig/version/delete')); + .get(require('./priceConfig/version/getVersion')) + .patch(require('./priceConfig/version/update')) + .delete(require('./priceConfig/version/delete')); // plan config router.route('/v5/projects/metadata/planConfig/:key/versions/:version(\\d+)/revisions/:revision(\\d+)') @@ -312,7 +312,7 @@ router.route('/v5/projects/metadata/planConfig/:key/versions/:version(\\d+)') // user level reports router.route('/v5/projects/reports/embed') -.get(require('./userReports/getEmbedReport')); + .get(require('./userReports/getEmbedReport')); // work streams router.route('/v5/projects/:projectId(\\d+)/workstreams') @@ -346,7 +346,7 @@ router.route('/v5/projects/:projectId(\\d+)/workstreams/:workStreamId(\\d+)/work router.route('/v5/projects/:projectId/reports/embed') -.get(require('./projectReports/getEmbedReport')); + .get(require('./projectReports/getEmbedReport')); router.route('/v5/projects/:projectId/reports') .get(require('./projectReports/getReport')); diff --git a/src/routes/metadata/list.js b/src/routes/metadata/list.js index 0d2e77de..d8fa73f4 100644 --- a/src/routes/metadata/list.js +++ b/src/routes/metadata/list.js @@ -1,4 +1,4 @@ - /** +/** * API to list all metadata */ import { middleware as tcMiddleware } from 'tc-core-library-js'; @@ -178,49 +178,49 @@ module.exports = [ // Disadvantage: // - we have to filter disabled Project Templates and Product Templates by JS util.fetchFromES(null, null, 'metadata') - .then((data) => { - const esDataToReturn = _.pick(data, metadataProperties); - // if some metadata properties are not returned from ES, then initialize such properties with empty array - // for consistency - metadataProperties.forEach((prop) => { - if (!esDataToReturn[prop]) { - esDataToReturn[prop] = []; - } - }); + .then((data) => { + const esDataToReturn = _.pick(data, metadataProperties); + // if some metadata properties are not returned from ES, then initialize such properties with empty array + // for consistency + metadataProperties.forEach((prop) => { + if (!esDataToReturn[prop]) { + esDataToReturn[prop] = []; + } + }); - // return only non-disabled Project Templates - if (esDataToReturn.projectTemplates && esDataToReturn.projectTemplates.length > 0) { - esDataToReturn.projectTemplates = _.filter(esDataToReturn.projectTemplates, { disabled: false }); - } + // return only non-disabled Project Templates + if (esDataToReturn.projectTemplates && esDataToReturn.projectTemplates.length > 0) { + esDataToReturn.projectTemplates = _.filter(esDataToReturn.projectTemplates, { disabled: false }); + } - // return only non-disabled Product Templates - if (esDataToReturn.productTemplates && esDataToReturn.productTemplates.length > 0) { - esDataToReturn.productTemplates = _.filter(esDataToReturn.productTemplates, { disabled: false }); - } + // return only non-disabled Product Templates + if (esDataToReturn.productTemplates && esDataToReturn.productTemplates.length > 0) { + esDataToReturn.productTemplates = _.filter(esDataToReturn.productTemplates, { disabled: false }); + } - // WARNING: `BuildingBlock` model contains sensitive data! - // - // We should NEVER return `privateConfig` property for `buildingBlocks`. - // For the DB we use hooks to always clear it out, see `src/models/buildingBlock.js`. - // For the ES so far we should always remember about it and filter it out. - if (esDataToReturn.buildingBlocks && esDataToReturn.buildingBlocks.length > 0) { - esDataToReturn.buildingBlocks = _.map( - esDataToReturn.buildingBlocks, - buildingBlock => _.omit(buildingBlock, 'privateConfig'), - ); - } + // WARNING: `BuildingBlock` model contains sensitive data! + // + // We should NEVER return `privateConfig` property for `buildingBlocks`. + // For the DB we use hooks to always clear it out, see `src/models/buildingBlock.js`. + // For the ES so far we should always remember about it and filter it out. + if (esDataToReturn.buildingBlocks && esDataToReturn.buildingBlocks.length > 0) { + esDataToReturn.buildingBlocks = _.map( + esDataToReturn.buildingBlocks, + buildingBlock => _.omit(buildingBlock, 'privateConfig'), + ); + } - // check if any data is returned from ES - const hasDataInES = _.some(esDataToReturn, propData => propData && propData.length > 0); + // check if any data is returned from ES + const hasDataInES = _.some(esDataToReturn, propData => propData && propData.length > 0); - if (hasDataInES) { - req.log.debug('Metadata is found in ES'); - return res.json(esDataToReturn); - } + if (hasDataInES) { + req.log.debug('Metadata is found in ES'); + return res.json(esDataToReturn); + } - req.log.debug('Metadata is not found in ES'); - return loadMetadataFromDb(req.query.includeAllReferred).then(dbDataToReturn => res.json(dbDataToReturn)); - }) - .catch(next); + req.log.debug('Metadata is not found in ES'); + return loadMetadataFromDb(req.query.includeAllReferred).then(dbDataToReturn => res.json(dbDataToReturn)); + }) + .catch(next); }, ]; diff --git a/src/routes/metadata/list.spec.js b/src/routes/metadata/list.spec.js index 67185f65..400034fb 100644 --- a/src/routes/metadata/list.spec.js +++ b/src/routes/metadata/list.spec.js @@ -239,17 +239,17 @@ const getObjToIndex = (items) => { describe('GET all metadata from DB', () => { before((done) => { testUtil.clearES() - .then(() => testUtil.clearDb()) - .then(() => models.ProjectTemplate.bulkCreate(projectTemplates)) - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)) - .then(() => models.ProjectType.bulkCreate(projectTypes)) - .then(() => models.ProductCategory.bulkCreate(productCategories)) - .then(() => models.Form.bulkCreate(forms)) - .then(() => models.PriceConfig.bulkCreate(priceConfigs)) - .then(() => models.PlanConfig.bulkCreate(planConfigs)) - .then(() => models.BuildingBlock.bulkCreate(buildingBlocks)) - .then(() => done()); + .then(() => testUtil.clearDb()) + .then(() => models.ProjectTemplate.bulkCreate(projectTemplates)) + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)) + .then(() => models.ProjectType.bulkCreate(projectTypes)) + .then(() => models.ProductCategory.bulkCreate(productCategories)) + .then(() => models.Form.bulkCreate(forms)) + .then(() => models.PriceConfig.bulkCreate(priceConfigs)) + .then(() => models.PlanConfig.bulkCreate(planConfigs)) + .then(() => models.BuildingBlock.bulkCreate(buildingBlocks)) + .then(() => done()); }); after((done) => { @@ -381,43 +381,43 @@ describe('GET all metadata from ES', () => { const esData = {}; testUtil.clearES() - .then(() => testUtil.clearDb()) - .then(() => models.ProjectTemplate.bulkCreate(projectTemplates, { returning: true })) - .then((created) => { esData.projectTemplates = getObjToIndex(created); }) - .then(() => models.ProductTemplate.bulkCreate(productTemplates, { returning: true })) - .then((created) => { esData.productTemplates = getObjToIndex(created); }) - .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates, { returning: true })) - .then((created) => { esData.milestoneTemplates = getObjToIndex(created); }) - .then(() => models.ProjectType.bulkCreate(projectTypes, { returning: true })) - .then((created) => { esData.projectTypes = getObjToIndex(created); }) - .then(() => models.ProductCategory.bulkCreate(productCategories, { returning: true })) - .then((created) => { esData.productCategories = getObjToIndex(created); }) - .then(() => models.Form.bulkCreate(forms, { returning: true })) - .then((created) => { + .then(() => testUtil.clearDb()) + .then(() => models.ProjectTemplate.bulkCreate(projectTemplates, { returning: true })) + .then((created) => { esData.projectTemplates = getObjToIndex(created); }) + .then(() => models.ProductTemplate.bulkCreate(productTemplates, { returning: true })) + .then((created) => { esData.productTemplates = getObjToIndex(created); }) + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates, { returning: true })) + .then((created) => { esData.milestoneTemplates = getObjToIndex(created); }) + .then(() => models.ProjectType.bulkCreate(projectTypes, { returning: true })) + .then((created) => { esData.projectTypes = getObjToIndex(created); }) + .then(() => models.ProductCategory.bulkCreate(productCategories, { returning: true })) + .then((created) => { esData.productCategories = getObjToIndex(created); }) + .then(() => models.Form.bulkCreate(forms, { returning: true })) + .then((created) => { // only index form with key `productKey 1` - const v2Form = _(created).filter(c => c.key === 'productKey 1'); - esData.forms = getObjToIndex(v2Form); - }) - .then(() => models.PriceConfig.bulkCreate(priceConfigs, { returning: true })) - .then((created) => { + const v2Form = _(created).filter(c => c.key === 'productKey 1'); + esData.forms = getObjToIndex(v2Form); + }) + .then(() => models.PriceConfig.bulkCreate(priceConfigs, { returning: true })) + .then((created) => { // only index latest versions - const v2PriceConfigs = _(created).filter(c => c.version === 2); - esData.priceConfigs = getObjToIndex(v2PriceConfigs); - }) - .then(() => models.PlanConfig.bulkCreate(planConfigs, { returning: true })) - .then((created) => { + const v2PriceConfigs = _(created).filter(c => c.version === 2); + esData.priceConfigs = getObjToIndex(v2PriceConfigs); + }) + .then(() => models.PlanConfig.bulkCreate(planConfigs, { returning: true })) + .then((created) => { // only index latest versions - const v2PlanConfigs = _(created).filter(c => c.version === 2); - esData.planConfigs = getObjToIndex(v2PlanConfigs); - }) - .then(() => models.BuildingBlock.bulkCreate(buildingBlocks, { returning: true })) - .then((created) => { esData.buildingBlocks = getObjToIndex(created); }) - .then(() => server.services.es.index({ - index: ES_METADATA_INDEX, - type: ES_METADATA_TYPE, - body: esData, - })) - .then(() => done()); + const v2PlanConfigs = _(created).filter(c => c.version === 2); + esData.planConfigs = getObjToIndex(v2PlanConfigs); + }) + .then(() => models.BuildingBlock.bulkCreate(buildingBlocks, { returning: true })) + .then((created) => { esData.buildingBlocks = getObjToIndex(created); }) + .then(() => server.services.es.index({ + index: ES_METADATA_INDEX, + type: ES_METADATA_TYPE, + body: esData, + })) + .then(() => done()); }); after((done) => { diff --git a/src/routes/milestoneTemplates/clone.spec.js b/src/routes/milestoneTemplates/clone.spec.js index 4f233354..85ffaf33 100644 --- a/src/routes/milestoneTemplates/clone.spec.js +++ b/src/routes/milestoneTemplates/clone.spec.js @@ -115,8 +115,8 @@ const milestoneTemplates = [ describe('CLONE milestone template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestoneTemplates/create.js b/src/routes/milestoneTemplates/create.js index d72f727a..0c5b0ac9 100644 --- a/src/routes/milestoneTemplates/create.js +++ b/src/routes/milestoneTemplates/create.js @@ -81,26 +81,26 @@ module.exports = [ return Promise.resolve(); }), ) - .then((otherUpdated) => { + .then((otherUpdated) => { // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_ADDED, - RESOURCES.MILESTONE_TEMPLATE, - result); - - // emit the event for other milestone templates order updated - _.map(otherUpdated, milestoneTemplate => util.sendResourceToKafkaBus( req, - EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_UPDATED, + EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_ADDED, RESOURCES.MILESTONE_TEMPLATE, - _.assign(_.pick(milestoneTemplate.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt'))), - ); + result); + + // emit the event for other milestone templates order updated + _.map(otherUpdated, milestoneTemplate => + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_UPDATED, + RESOURCES.MILESTONE_TEMPLATE, + _.assign(_.pick(milestoneTemplate.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt'))), + ); - // Write to response - res.status(201).json(result); - }) - .catch(next); + // Write to response + res.status(201).json(result); + }) + .catch(next); }, ]; diff --git a/src/routes/milestoneTemplates/create.spec.js b/src/routes/milestoneTemplates/create.spec.js index 0b1bb3cb..d0cf0a56 100644 --- a/src/routes/milestoneTemplates/create.spec.js +++ b/src/routes/milestoneTemplates/create.spec.js @@ -96,8 +96,8 @@ const milestoneTemplates = [ describe('CREATE milestone template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestoneTemplates/delete.js b/src/routes/milestoneTemplates/delete.js index 0ab2f7e0..acf84b74 100644 --- a/src/routes/milestoneTemplates/delete.js +++ b/src/routes/milestoneTemplates/delete.js @@ -22,19 +22,19 @@ module.exports = [ validateMilestoneTemplate.validateIdParam, permissions('milestoneTemplate.delete'), (req, res, next) => models.sequelize.transaction(() => - // soft delete the record - req.milestoneTemplate.update({ deletedBy: req.authUser.userId }) - .then(entity => entity.destroy()), - ) - .then(() => { - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_REMOVED, - RESOURCES.MILESTONE_TEMPLATE, - { id: req.params.milestoneTemplateId }); + // soft delete the record + req.milestoneTemplate.update({ deletedBy: req.authUser.userId }) + .then(entity => entity.destroy()), + ) + .then(() => { + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.MILESTONE_TEMPLATE_REMOVED, + RESOURCES.MILESTONE_TEMPLATE, + { id: req.params.milestoneTemplateId }); - res.status(204).end(); - }) - .catch(next), + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/milestoneTemplates/delete.spec.js b/src/routes/milestoneTemplates/delete.spec.js index 25f61ad4..c2e379f9 100644 --- a/src/routes/milestoneTemplates/delete.spec.js +++ b/src/routes/milestoneTemplates/delete.spec.js @@ -124,8 +124,8 @@ const milestoneTemplates = [ describe('DELETE milestone template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestoneTemplates/get.js b/src/routes/milestoneTemplates/get.js index 2efdb3d3..fa7ce46e 100644 --- a/src/routes/milestoneTemplates/get.js +++ b/src/routes/milestoneTemplates/get.js @@ -32,13 +32,13 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No milestoneTemplate found in ES'); - return res.json(_.omit(req.milestoneTemplate.toJSON(), 'deletedAt', 'deletedBy')); - } - req.log.debug('milestoneTemplate found in ES'); - return res.json(data[0].inner_hits.milestoneTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - }) - .catch(next), + .then((data) => { + if (data.length === 0) { + req.log.debug('No milestoneTemplate found in ES'); + return res.json(_.omit(req.milestoneTemplate.toJSON(), 'deletedAt', 'deletedBy')); + } + req.log.debug('milestoneTemplate found in ES'); + return res.json(data[0].inner_hits.milestoneTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + }) + .catch(next), ]; diff --git a/src/routes/milestoneTemplates/get.spec.js b/src/routes/milestoneTemplates/get.spec.js index 42aada94..8b17a5e3 100644 --- a/src/routes/milestoneTemplates/get.spec.js +++ b/src/routes/milestoneTemplates/get.spec.js @@ -99,8 +99,8 @@ const milestoneTemplates = [ describe('GET milestone template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestoneTemplates/list.js b/src/routes/milestoneTemplates/list.js index 572b8364..1524195a 100644 --- a/src/routes/milestoneTemplates/list.js +++ b/src/routes/milestoneTemplates/list.js @@ -72,30 +72,30 @@ module.exports = [ } return util.fetchFromES('milestoneTemplates', query) - .then((data) => { - let milestoneTemplates = _.isArray(data.milestoneTemplates) ? - data.milestoneTemplates : data.milestoneTemplates.hits.hits; - if (milestoneTemplates.length === 0) { - req.log.debug('No milestoneTemplate found in ES'); - return models.MilestoneTemplate.findAll({ - where, - order: [sortColumnAndOrder], - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then(result => res.json(result)) - .catch(next); - } - req.log.debug('milestoneTemplates found in ES'); + .then((data) => { + let milestoneTemplates = _.isArray(data.milestoneTemplates) ? + data.milestoneTemplates : data.milestoneTemplates.hits.hits; + if (milestoneTemplates.length === 0) { + req.log.debug('No milestoneTemplate found in ES'); + return models.MilestoneTemplate.findAll({ + where, + order: [sortColumnAndOrder], + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then(result => res.json(result)) + .catch(next); + } + req.log.debug('milestoneTemplates found in ES'); // Get the milestoneTemplates - milestoneTemplates = _.map(milestoneTemplates, (m) => { - if (m._source) return m._source; // eslint-disable-line no-underscore-dangle - return m; - }); + milestoneTemplates = _.map(milestoneTemplates, (m) => { + if (m._source) return m._source; // eslint-disable-line no-underscore-dangle + return m; + }); // Sort - milestoneTemplates = _.orderBy(milestoneTemplates, [sortColumnAndOrder[0]], [sortColumnAndOrder[1]]); - return res.json(milestoneTemplates); - }) - .catch(next); + milestoneTemplates = _.orderBy(milestoneTemplates, [sortColumnAndOrder[0]], [sortColumnAndOrder[1]]); + return res.json(milestoneTemplates); + }) + .catch(next); }, ]; diff --git a/src/routes/milestoneTemplates/list.spec.js b/src/routes/milestoneTemplates/list.spec.js index 5a4b1567..eec38ef0 100644 --- a/src/routes/milestoneTemplates/list.spec.js +++ b/src/routes/milestoneTemplates/list.spec.js @@ -118,8 +118,8 @@ describe('LIST milestone template', () => { }); beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestoneTemplates/update.spec.js b/src/routes/milestoneTemplates/update.spec.js index ef76a799..d771dc6d 100644 --- a/src/routes/milestoneTemplates/update.spec.js +++ b/src/routes/milestoneTemplates/update.spec.js @@ -162,8 +162,8 @@ const milestoneTemplates = [ describe('UPDATE milestone template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); + .then(() => models.ProductTemplate.bulkCreate(productTemplates)) + .then(() => { models.MilestoneTemplate.bulkCreate(milestoneTemplates).then(() => done()); }); }, ); after((done) => { diff --git a/src/routes/milestones/create.js b/src/routes/milestones/create.js index 08cda372..d590494b 100644 --- a/src/routes/milestones/create.js +++ b/src/routes/milestones/create.js @@ -101,47 +101,47 @@ module.exports = [ return Promise.resolve(); }), ) - .then((otherUpdated) => { + .then((otherUpdated) => { // Send event to bus - req.log.debug('Sending event to RabbitMQ bus for milestone %d', result.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_ADDED, - result, - { correlationId: req.id }, - ); + req.log.debug('Sending event to RabbitMQ bus for milestone %d', result.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_ADDED, + result, + { correlationId: req.id }, + ); - // NOTE So far this logic is implemented in RabbitMQ handler of MILESTONE_ADDED - // Even though we send this event to the Kafka, the "project-processor-es" shouldn't process it. - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.MILESTONE_ADDED, - RESOURCES.MILESTONE, - result); - - // NOTE So far this logic is implemented in RabbitMQ handler of MILESTONE_ADDED - // Even though we send these events to the Kafka, the "project-processor-es" shouldn't process them. - // - // We don't process these event in "project-processor-es" - // because it will make 'version conflict' error in ES. - // The order of the other milestones need to be updated in the PROJECT_PHASE_UPDATED event handler - _.map(otherUpdated, milestone => + // NOTE So far this logic is implemented in RabbitMQ handler of MILESTONE_ADDED + // Even though we send this event to the Kafka, the "project-processor-es" shouldn't process it. util.sendResourceToKafkaBus( req, - EVENT.ROUTING_KEY.MILESTONE_UPDATED, + EVENT.ROUTING_KEY.MILESTONE_ADDED, RESOURCES.MILESTONE, - _.assign(_.pick(milestone.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt')), - // Pass the same object as original milestone even though, their time has changed. - // So far we don't use time properties in the handler so it's ok. But in general, we should pass - // the original milestones. <- TODO - _.assign(_.pick(milestone.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt')), - null, // no route - true, // don't send event to Notification Service as the main event here is updating one milestone - ), - ); + result); + + // NOTE So far this logic is implemented in RabbitMQ handler of MILESTONE_ADDED + // Even though we send these events to the Kafka, the "project-processor-es" shouldn't process them. + // + // We don't process these event in "project-processor-es" + // because it will make 'version conflict' error in ES. + // The order of the other milestones need to be updated in the PROJECT_PHASE_UPDATED event handler + _.map(otherUpdated, milestone => + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.MILESTONE_UPDATED, + RESOURCES.MILESTONE, + _.assign(_.pick(milestone.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt')), + // Pass the same object as original milestone even though, their time has changed. + // So far we don't use time properties in the handler so it's ok. But in general, we should pass + // the original milestones. <- TODO + _.assign(_.pick(milestone.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt')), + null, // no route + true, // don't send event to Notification Service as the main event here is updating one milestone + ), + ); - // Write to the response - res.status(201).json(result); - return Promise.resolve(); - }) - .catch(next); + // Write to the response + res.status(201).json(result); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/milestones/create.spec.js b/src/routes/milestones/create.spec.js index 271e1d2f..f1fa7138 100644 --- a/src/routes/milestones/create.spec.js +++ b/src/routes/milestones/create.spec.js @@ -72,72 +72,72 @@ describe('CREATE milestone', () => { }, ]).then(() => // Create phase - models.ProjectPhase.bulkCreate([ - { - projectId: projectId1, - name: 'test project phase 1', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json 2', - }, - createdBy: 1, - updatedBy: 1, - }, - { - projectId: projectId2, - name: 'test project phase 2', - status: 'active', - startDate: '2018-05-16T00:00:00Z', - endDate: '2018-05-16T12:00:00Z', - budget: 21.0, - progress: 1.234567, - details: { - message: 'This can be any json 2', - }, - createdBy: 2, - updatedBy: 2, - deletedAt: '2018-05-15T00:00:00Z', - }, - ])) + models.ProjectPhase.bulkCreate([ + { + projectId: projectId1, + name: 'test project phase 1', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json 2', + }, + createdBy: 1, + updatedBy: 1, + }, + { + projectId: projectId2, + name: 'test project phase 2', + status: 'active', + startDate: '2018-05-16T00:00:00Z', + endDate: '2018-05-16T12:00:00Z', + budget: 21.0, + progress: 1.234567, + details: { + message: 'This can be any json 2', + }, + createdBy: 2, + updatedBy: 2, + deletedAt: '2018-05-15T00:00:00Z', + }, + ])) .then(() => // Create timelines - models.Timeline.bulkCreate([ - { - name: 'name 1', - description: 'description 1', - startDate: '2018-05-02T00:00:00.000Z', - endDate: '2018-06-12T00:00:00.000Z', - reference: 'project', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - }, - { - name: 'name 2', - description: 'description 2', - startDate: '2018-05-12T00:00:00.000Z', - endDate: '2018-06-13T00:00:00.000Z', - reference: 'phase', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - }, - { - name: 'name 3', - description: 'description 3', - startDate: '2018-05-13T00:00:00.000Z', - endDate: '2018-06-14T00:00:00.000Z', - reference: 'phase', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - deletedAt: '2018-05-14T00:00:00.000Z', - }, - ])) + models.Timeline.bulkCreate([ + { + name: 'name 1', + description: 'description 1', + startDate: '2018-05-02T00:00:00.000Z', + endDate: '2018-06-12T00:00:00.000Z', + reference: 'project', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + }, + { + name: 'name 2', + description: 'description 2', + startDate: '2018-05-12T00:00:00.000Z', + endDate: '2018-06-13T00:00:00.000Z', + reference: 'phase', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + }, + { + name: 'name 3', + description: 'description 3', + startDate: '2018-05-13T00:00:00.000Z', + endDate: '2018-06-14T00:00:00.000Z', + reference: 'phase', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + deletedAt: '2018-05-14T00:00:00.000Z', + }, + ])) .then(() => { // Create milestones models.Milestone.bulkCreate([ diff --git a/src/routes/milestones/delete.js b/src/routes/milestones/delete.js index ade643c7..d8436437 100644 --- a/src/routes/milestones/delete.js +++ b/src/routes/milestones/delete.js @@ -48,25 +48,25 @@ module.exports = [ .then(() => milestone.destroy()); }), ) - .then((deleted) => { + .then((deleted) => { // Send event to bus - req.log.debug('Sending event to RabbitMQ bus for milestone %d', deleted.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_REMOVED, - deleted, - { correlationId: req.id }, - ); + req.log.debug('Sending event to RabbitMQ bus for milestone %d', deleted.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_REMOVED, + deleted, + { correlationId: req.id }, + ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.MILESTONE_REMOVED, - RESOURCES.MILESTONE, - { id: deleted.id }); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.MILESTONE_REMOVED, + RESOURCES.MILESTONE, + { id: deleted.id }); - // Write to response - res.status(204).end(); - return Promise.resolve(); - }) - .catch(next); + // Write to response + res.status(204).end(); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/milestones/get.js b/src/routes/milestones/get.js index 5c33994d..c283b8f9 100644 --- a/src/routes/milestones/get.js +++ b/src/routes/milestones/get.js @@ -41,29 +41,29 @@ module.exports = [ }, }, }, 'timeline') - .then((data) => { - if (data.length === 0) { - req.log.debug('No milestone found in ES'); - // Find the milestone - models.Milestone.findOne({ where }) - .then((milestone) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No milestone found in ES'); + // Find the milestone + models.Milestone.findOne({ where }) + .then((milestone) => { // Not found - if (!milestone) { - const apiErr = new Error(`Milestone not found for milestone id ${req.params.milestoneId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!milestone) { + const apiErr = new Error(`Milestone not found for milestone id ${req.params.milestoneId}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - // Write to response - res.json(_.omit(milestone.toJSON(), ['deletedBy', 'deletedAt'])); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('milestone found in ES'); - res.json(data[0].inner_hits.milestones.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + // Write to response + res.json(_.omit(milestone.toJSON(), ['deletedBy', 'deletedAt'])); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('milestone found in ES'); + res.json(data[0].inner_hits.milestones.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/milestones/get.spec.js b/src/routes/milestones/get.spec.js index f541eb02..3607a427 100644 --- a/src/routes/milestones/get.spec.js +++ b/src/routes/milestones/get.spec.js @@ -63,72 +63,72 @@ describe('GET milestone', () => { ]) .then(() => // Create phase - models.ProjectPhase.bulkCreate([ - { - projectId: 1, - name: 'test project phase 1', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json 2', - }, - createdBy: 1, - updatedBy: 1, - }, - { - projectId: 2, - name: 'test project phase 2', - status: 'active', - startDate: '2018-05-16T00:00:00Z', - endDate: '2018-05-16T12:00:00Z', - budget: 21.0, - progress: 1.234567, - details: { - message: 'This can be any json 2', - }, - createdBy: 2, - updatedBy: 2, - deletedAt: '2018-05-15T00:00:00Z', - }, - ])) + models.ProjectPhase.bulkCreate([ + { + projectId: 1, + name: 'test project phase 1', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json 2', + }, + createdBy: 1, + updatedBy: 1, + }, + { + projectId: 2, + name: 'test project phase 2', + status: 'active', + startDate: '2018-05-16T00:00:00Z', + endDate: '2018-05-16T12:00:00Z', + budget: 21.0, + progress: 1.234567, + details: { + message: 'This can be any json 2', + }, + createdBy: 2, + updatedBy: 2, + deletedAt: '2018-05-15T00:00:00Z', + }, + ])) .then(() => // Create timelines - models.Timeline.bulkCreate([ - { - name: 'name 1', - description: 'description 1', - startDate: '2018-05-11T00:00:00.000Z', - endDate: '2018-05-12T00:00:00.000Z', - reference: 'project', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - }, - { - name: 'name 2', - description: 'description 2', - startDate: '2018-05-12T00:00:00.000Z', - endDate: '2018-05-13T00:00:00.000Z', - reference: 'phase', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - }, - { - name: 'name 3', - description: 'description 3', - startDate: '2018-05-13T00:00:00.000Z', - endDate: '2018-05-14T00:00:00.000Z', - reference: 'phase', - referenceId: 1, - createdBy: 1, - updatedBy: 1, - deletedAt: '2018-05-14T00:00:00.000Z', - }, - ])) + models.Timeline.bulkCreate([ + { + name: 'name 1', + description: 'description 1', + startDate: '2018-05-11T00:00:00.000Z', + endDate: '2018-05-12T00:00:00.000Z', + reference: 'project', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + }, + { + name: 'name 2', + description: 'description 2', + startDate: '2018-05-12T00:00:00.000Z', + endDate: '2018-05-13T00:00:00.000Z', + reference: 'phase', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + }, + { + name: 'name 3', + description: 'description 3', + startDate: '2018-05-13T00:00:00.000Z', + endDate: '2018-05-14T00:00:00.000Z', + reference: 'phase', + referenceId: 1, + createdBy: 1, + updatedBy: 1, + deletedAt: '2018-05-14T00:00:00.000Z', + }, + ])) .then(() => { // Create milestones models.Milestone.bulkCreate([ diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index 6704a964..7c80bbb5 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -323,38 +323,38 @@ module.exports = [ return Promise.resolve({}); }), ) - .then(({ originalMilestones, updatedMilestones }) => { - const cascadedMilestones = _.map(originalMilestones, om => ({ - original: om, updated: _.find(updatedMilestones, um => um.id === om.id), - })); - const cascadedUpdates = { milestones: cascadedMilestones }; - // if there is a change in timeline, add it to the cascadedUpdates - if (originalTimeline.updatedAt !== timeline.updatedAt) { - cascadedUpdates.timeline = { - original: originalTimeline, - updated: _.omit(timeline.toJSON(), 'deletedAt', 'deletedBy'), - }; - } - // Send event to bus - req.log.debug('Sending event to RabbitMQ bus for milestone %d', updated.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_UPDATED, - { original, updated, cascadedUpdates }, - { correlationId: req.id }, - ); + .then(({ originalMilestones, updatedMilestones }) => { + const cascadedMilestones = _.map(originalMilestones, om => ({ + original: om, updated: _.find(updatedMilestones, um => um.id === om.id), + })); + const cascadedUpdates = { milestones: cascadedMilestones }; + // if there is a change in timeline, add it to the cascadedUpdates + if (originalTimeline.updatedAt !== timeline.updatedAt) { + cascadedUpdates.timeline = { + original: originalTimeline, + updated: _.omit(timeline.toJSON(), 'deletedAt', 'deletedBy'), + }; + } + // Send event to bus + req.log.debug('Sending event to RabbitMQ bus for milestone %d', updated.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.MILESTONE_UPDATED, + { original, updated, cascadedUpdates }, + { correlationId: req.id }, + ); - // emit the event - // we cannot use `util.sendResourceToKafkaBus` as we have to pass a custom param `cascadedUpdates` - req.app.emit(EVENT.ROUTING_KEY.MILESTONE_UPDATED, { - req, - resource: _.assign({ resource: RESOURCES.MILESTONE }, updated), - originalResource: _.assign({ resource: RESOURCES.MILESTONE }, original), - cascadedUpdates, - }); + // emit the event + // we cannot use `util.sendResourceToKafkaBus` as we have to pass a custom param `cascadedUpdates` + req.app.emit(EVENT.ROUTING_KEY.MILESTONE_UPDATED, { + req, + resource: _.assign({ resource: RESOURCES.MILESTONE }, updated), + originalResource: _.assign({ resource: RESOURCES.MILESTONE }, original), + cascadedUpdates, + }); - // Write to response - res.json(updated); - return Promise.resolve(); - }) - .catch(next); + // Write to response + res.json(updated); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/milestones/update.spec.js b/src/routes/milestones/update.spec.js index bf8b1c7e..2a5c12b4 100644 --- a/src/routes/milestones/update.spec.js +++ b/src/routes/milestones/update.spec.js @@ -1145,12 +1145,12 @@ describe('UPDATE Milestone', () => { const newBody = _.cloneDeep(body); newBody.status = 'paused'; request(server) - .patch('/v5/timelines/1/milestones/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(newBody) - .expect(400, done); + .patch('/v5/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(newBody) + .expect(400, done); }); it('should return 400 if try to pause not active milestone', (done) => { @@ -1158,12 +1158,12 @@ describe('UPDATE Milestone', () => { newBody.status = 'paused'; newBody.statusComment = 'milestone paused'; request(server) - .patch('/v5/timelines/1/milestones/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(newBody) - .expect(400, done); + .patch('/v5/timelines/1/milestones/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(newBody) + .expect(400, done); }); it('should return 200 if try to pause and should have one status history created', (done) => { @@ -1171,45 +1171,45 @@ describe('UPDATE Milestone', () => { newBody.status = 'paused'; newBody.statusComment = 'milestone paused'; request(server) - .patch('/v5/timelines/1/milestones/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(newBody) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - models.Milestone.findByPk(1).then((milestone) => { - milestone.status.should.be.eql('paused'); - return models.StatusHistory.findAll({ - where: { - reference: 'milestone', - referenceId: milestone.id, - status: milestone.status, - comment: 'milestone paused', - }, - paranoid: false, - }).then((statusHistories) => { - statusHistories.length.should.be.eql(1); - done(); + .patch('/v5/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(newBody) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + models.Milestone.findByPk(1).then((milestone) => { + milestone.status.should.be.eql('paused'); + return models.StatusHistory.findAll({ + where: { + reference: 'milestone', + referenceId: milestone.id, + status: milestone.status, + comment: 'milestone paused', + }, + paranoid: false, + }).then((statusHistories) => { + statusHistories.length.should.be.eql(1); + done(); + }); }); - }); - } - }); + } + }); }); it('should return 400 if try to resume not paused milestone', (done) => { const newBody = _.cloneDeep(body); newBody.status = 'resume'; request(server) - .patch('/v5/timelines/1/milestones/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(newBody) - .expect(400, done); + .patch('/v5/timelines/1/milestones/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(newBody) + .expect(400, done); }); it('should return 200 if try to resume then status should update to last status and ' + @@ -1250,34 +1250,34 @@ describe('UPDATE Milestone', () => { .then(milestone => milestone.update(_.assign({}, milestone.toJSON(), { status: 'paused' }))), ).then(() => { request(server) - .patch('/v5/timelines/1/milestones/7') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(newBody) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - models.Milestone.findByPk(7).then((milestone) => { - milestone.status.should.be.eql('active'); + .patch('/v5/timelines/1/milestones/7') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(newBody) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + models.Milestone.findByPk(7).then((milestone) => { + milestone.status.should.be.eql('active'); - return models.StatusHistory.findAll({ - where: { - reference: 'milestone', - referenceId: milestone.id, - status: 'active', - comment: 'new comment', - }, - paranoid: false, - }).then((statusHistories) => { - statusHistories.length.should.be.eql(1); - done(); + return models.StatusHistory.findAll({ + where: { + reference: 'milestone', + referenceId: milestone.id, + status: 'active', + comment: 'new comment', + }, + paranoid: false, + }).then((statusHistories) => { + statusHistories.length.should.be.eql(1); + done(); + }).catch(done); }).catch(done); - }).catch(done); - } - }); + } + }); }); }); diff --git a/src/routes/orgConfig/delete.js b/src/routes/orgConfig/delete.js index 91c59bb4..5aeb2c85 100644 --- a/src/routes/orgConfig/delete.js +++ b/src/routes/orgConfig/delete.js @@ -21,7 +21,7 @@ module.exports = [ validate(schema), permissions('orgConfig.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.OrgConfig.findByPk(req.params.id) .then((entity) => { if (!entity) { @@ -33,12 +33,12 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then((entity) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.ORG_CONFIG, - _.pick(entity.toJSON(), 'id')); - res.status(204).end(); - }) - .catch(next), + .then((entity) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.ORG_CONFIG, + _.pick(entity.toJSON(), 'id')); + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/orgConfig/delete.spec.js b/src/routes/orgConfig/delete.spec.js index e4a234b5..e88db5db 100644 --- a/src/routes/orgConfig/delete.spec.js +++ b/src/routes/orgConfig/delete.spec.js @@ -10,27 +10,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (id, err, next) => { if (err) throw err; setTimeout(() => - models.OrgConfig.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.OrgConfig.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/metadata/orgConfig/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/metadata/orgConfig/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE organization config', () => { diff --git a/src/routes/orgConfig/get.js b/src/routes/orgConfig/get.js index 5bd8d582..960c2a72 100644 --- a/src/routes/orgConfig/get.js +++ b/src/routes/orgConfig/get.js @@ -30,33 +30,33 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No orgConfig found in ES'); - models.OrgConfig.findOne({ - where: { - id: req.params.id, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((orgConfig) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No orgConfig found in ES'); + models.OrgConfig.findOne({ + where: { + id: req.params.id, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((orgConfig) => { // Not found - if (!orgConfig) { - const apiErr = new Error(`Organization config not found for id ${req.params.id}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!orgConfig) { + const apiErr = new Error(`Organization config not found for id ${req.params.id}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(orgConfig); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('orgConfigs found in ES'); - res.json(data[0].inner_hits.orgConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(orgConfig); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('orgConfigs found in ES'); + res.json(data[0].inner_hits.orgConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, diff --git a/src/routes/orgConfig/list.js b/src/routes/orgConfig/list.js index 6e5fb747..6df8d2bf 100644 --- a/src/routes/orgConfig/list.js +++ b/src/routes/orgConfig/list.js @@ -60,25 +60,25 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.orgConfigs.length === 0) { - req.log.debug('No orgConfig found in ES'); + .then((data) => { + if (data.orgConfigs.length === 0) { + req.log.debug('No orgConfig found in ES'); - // Get all organization config - const where = filters ? _.assign({}, filters, { orgId: { $in: orgIds } }) : {}; - models.OrgConfig.findAll({ - where, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((orgConfigs) => { - res.json(orgConfigs); - }) - .catch(next); - } else { - req.log.debug('orgConfigs found in ES'); - res.json(data.orgConfigs.hits.hits.map(hit => hit._source)); // eslint-disable-line no-underscore-dangle - } - }); + // Get all organization config + const where = filters ? _.assign({}, filters, { orgId: { $in: orgIds } }) : {}; + models.OrgConfig.findAll({ + where, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then((orgConfigs) => { + res.json(orgConfigs); + }) + .catch(next); + } else { + req.log.debug('orgConfigs found in ES'); + res.json(data.orgConfigs.hits.hits.map(hit => hit._source)); // eslint-disable-line no-underscore-dangle + } + }); }, ]; diff --git a/src/routes/permissions/get.spec.js b/src/routes/permissions/get.spec.js index 57f7d62e..6edc6634 100644 --- a/src/routes/permissions/get.spec.js +++ b/src/routes/permissions/get.spec.js @@ -74,47 +74,47 @@ describe('GET permissions', () => { createdBy: 1, updatedBy: 2, }) - .then((t) => { - templateId = t.id; - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then((project) => { - projectId = project.id; - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + .then((t) => { + templateId = t.id; + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId, + details: {}, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateId })); - models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true }) - .then(() => done()); - }); + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateId })); + models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true }) + .then(() => done()); + }); + }); }); - }); }); }); diff --git a/src/routes/phaseProducts/create.js b/src/routes/phaseProducts/create.js index 4bfb9bc3..2273ec25 100644 --- a/src/routes/phaseProducts/create.js +++ b/src/routes/phaseProducts/create.js @@ -90,30 +90,30 @@ module.exports = [ throw err; } return models.PhaseProduct.create(data) - .then((_newPhaseProduct) => { - newPhaseProduct = _.cloneDeep(_newPhaseProduct); - req.log.debug('new phase product created (id# %d, name: %s)', - newPhaseProduct.id, newPhaseProduct.name); - newPhaseProduct = newPhaseProduct.get({ plain: true }); - newPhaseProduct = _.omit(newPhaseProduct, ['deletedAt', 'utm']); - }); + .then((_newPhaseProduct) => { + newPhaseProduct = _.cloneDeep(_newPhaseProduct); + req.log.debug('new phase product created (id# %d, name: %s)', + newPhaseProduct.id, newPhaseProduct.name); + newPhaseProduct = newPhaseProduct.get({ plain: true }); + newPhaseProduct = _.omit(newPhaseProduct, ['deletedAt', 'utm']); + }); })) - .then(() => { + .then(() => { // Send events to buses - req.log.debug('Sending event to RabbitMQ bus for phase product %d', newPhaseProduct.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, - newPhaseProduct, - { correlationId: req.id }, - ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, - RESOURCES.PHASE_PRODUCT, - newPhaseProduct); + req.log.debug('Sending event to RabbitMQ bus for phase product %d', newPhaseProduct.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, + newPhaseProduct, + { correlationId: req.id }, + ); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, + RESOURCES.PHASE_PRODUCT, + newPhaseProduct); - res.status(201).json(newPhaseProduct); - }) - .catch((err) => { next(err); }); + res.status(201).json(newPhaseProduct); + }) + .catch((err) => { next(err); }); }, ]; diff --git a/src/routes/phaseProducts/create.spec.js b/src/routes/phaseProducts/create.spec.js index e987ed42..f5fda334 100644 --- a/src/routes/phaseProducts/create.spec.js +++ b/src/routes/phaseProducts/create.spec.js @@ -41,58 +41,58 @@ describe('Phase Products', () => { beforeEach((done) => { // mocks testUtil.clearDb() - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - projectId = p.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - phaseId = phase.id; - done(); - }); + }).then((phase) => { + phaseId = phase.id; + done(); }); }); }); + }); }); afterEach((done) => { @@ -302,13 +302,13 @@ describe('Phase Products', () => { it('should send correct BUS API messages when product phase created', (done) => { request(server) - .post(`/v5/projects/${projectId}/phases/${phaseId}/products`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) + .post(`/v5/projects/${projectId}/phases/${phaseId}/products`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) .end((err) => { if (err) { done(err); diff --git a/src/routes/phaseProducts/delete.js b/src/routes/phaseProducts/delete.js index c30138bb..5894037b 100644 --- a/src/routes/phaseProducts/delete.js +++ b/src/routes/phaseProducts/delete.js @@ -36,7 +36,7 @@ module.exports = [ } return existing.update({ deletedBy: req.authUser.userId }); }) - .then(entity => entity.destroy())) + .then(entity => entity.destroy())) .then((deleted) => { req.log.debug('deleted phase product', JSON.stringify(deleted, null, 2)); diff --git a/src/routes/phaseProducts/delete.spec.js b/src/routes/phaseProducts/delete.spec.js index c6e7c358..54bb62f6 100644 --- a/src/routes/phaseProducts/delete.spec.js +++ b/src/routes/phaseProducts/delete.spec.js @@ -22,15 +22,15 @@ const expectAfterDelete = (projectId, phaseId, id, err, next) => { }, paranoid: false, }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); - } - next(); - }), 500); + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); + } + next(); + }), 500); }; const body = { name: 'test phase product', @@ -65,63 +65,63 @@ describe('Phase Products', () => { beforeEach((done) => { // mocks testUtil.clearDb() - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - projectId = p.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - phaseId = phase.id; - _.assign(body, { phaseId, projectId }); + }).then((phase) => { + phaseId = phase.id; + _.assign(body, { phaseId, projectId }); - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - done(); - }); + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + done(); }); }); }); }); + }); }); afterEach((done) => { @@ -234,12 +234,12 @@ describe('Phase Products', () => { where: { userId: testUtil.userIds.copilot, projectId }, }).then(() => { request(server) - .delete(`/v5/projects/${projectId}/phases/${phaseId}/products/${productId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .expect(403) - .end(done); + .delete(`/v5/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(403) + .end(done); }); }); diff --git a/src/routes/phaseProducts/get.js b/src/routes/phaseProducts/get.js index a2eca0c7..0afd3974 100644 --- a/src/routes/phaseProducts/get.js +++ b/src/routes/phaseProducts/get.js @@ -59,11 +59,11 @@ module.exports = [ }).catch(err => next(err)); } req.log.debug('phase product found in ES'); - // Get the phases - const phases = data[0].inner_hits.phases.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle + // Get the phases + const phases = data[0].inner_hits.phases.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle const product = _.isArray(phases.products) ? _.find(phases.products, p => p.id === productId) : {}; if (!product) { - // handle 404 + // handle 404 const err = new Error('phase product not found for project id ' + `${projectId}, phase id ${phaseId} and product id ${productId}`); err.status = 404; diff --git a/src/routes/phaseProducts/get.spec.js b/src/routes/phaseProducts/get.spec.js index a7d8deab..e1e46b81 100644 --- a/src/routes/phaseProducts/get.spec.js +++ b/src/routes/phaseProducts/get.spec.js @@ -42,63 +42,63 @@ describe('Phase Products', () => { // mocks testUtil.clearDb() .then(() => testUtil.clearES()) - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - projectId = p.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - phaseId = phase.id; - _.assign(body, { phaseId, projectId }); + }).then((phase) => { + phaseId = phase.id; + _.assign(body, { phaseId, projectId }); - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - done(); - }); + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + done(); }); }); }); }); + }); }); after((done) => { diff --git a/src/routes/phaseProducts/list.js b/src/routes/phaseProducts/list.js index 842a0891..01afcb68 100644 --- a/src/routes/phaseProducts/list.js +++ b/src/routes/phaseProducts/list.js @@ -9,7 +9,7 @@ const retrieveFromDB = async (req, res, next) => { const projectId = _.parseInt(req.params.projectId); const phaseId = _.parseInt(req.params.phaseId); - // check if the project and phase are exist + // check if the project and phase are exist return models.ProjectPhase.findOne({ where: { id: phaseId, projectId }, raw: true, @@ -27,9 +27,9 @@ const retrieveFromDB = async (req, res, next) => { }; return models.PhaseProduct.search(parameters, req.log) - .then(({ rows }) => res.json(rows)); + .then(({ rows }) => res.json(rows)); }) - .catch(err => next(err)); + .catch(err => next(err)); }; module.exports = [ @@ -68,8 +68,8 @@ module.exports = [ return retrieveFromDB(req, res, next); } req.log.debug('phase product found in ES'); - // Get the phases - const phases = data[0].inner_hits.phases.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle + // Get the phases + const phases = data[0].inner_hits.phases.hits.hits[0]._source; // eslint-disable-line no-underscore-dangle const products = _.isArray(phases.products) ? phases.products : []; return res.json(products); }) diff --git a/src/routes/phaseProducts/update.js b/src/routes/phaseProducts/update.js index 7ae577da..0a648519 100644 --- a/src/routes/phaseProducts/update.js +++ b/src/routes/phaseProducts/update.js @@ -49,7 +49,7 @@ module.exports = [ }, }).then(existing => new Promise((accept, reject) => { if (!existing) { - // handle 404 + // handle 404 const err = new Error('No active phase product found for project id ' + `${projectId}, phase id ${phaseId} and product id ${productId}`); err.status = 404; @@ -61,28 +61,28 @@ module.exports = [ existing.save().then(accept).catch(reject); } }))) - .then((updated) => { - req.log.debug('updated phase product', JSON.stringify(updated, null, 2)); + .then((updated) => { + req.log.debug('updated phase product', JSON.stringify(updated, null, 2)); - const updatedValue = updated.get({ plain: true }); + const updatedValue = updated.get({ plain: true }); - // emit original and updated project phase information - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, - { original: previousValue, updated: updatedValue }, - { correlationId: req.id }, - ); + // emit original and updated project phase information + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, + { original: previousValue, updated: updatedValue }, + { correlationId: req.id }, + ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, - RESOURCES.PHASE_PRODUCT, - updatedValue, - previousValue, - ROUTES.PHASE_PRODUCTS.UPDATE); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, + RESOURCES.PHASE_PRODUCT, + updatedValue, + previousValue, + ROUTES.PHASE_PRODUCTS.UPDATE); - res.json(updated); - }).catch(err => next(err)); + res.json(updated); + }).catch(err => next(err)); }, ]; diff --git a/src/routes/phaseProducts/update.spec.js b/src/routes/phaseProducts/update.spec.js index be892212..1c5c2a61 100644 --- a/src/routes/phaseProducts/update.spec.js +++ b/src/routes/phaseProducts/update.spec.js @@ -54,63 +54,63 @@ describe('Phase Products', () => { beforeEach((done) => { // mocks testUtil.clearDb() - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - projectId = p.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - phaseId = phase.id; - _.assign(body, { phaseId, projectId }); - - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - done(); - }); + }).then((phase) => { + phaseId = phase.id; + _.assign(body, { phaseId, projectId }); + + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + done(); }); }); }); }); + }); }); after((done) => { diff --git a/src/routes/phases/create.js b/src/routes/phases/create.js index 080e1915..353a81b5 100644 --- a/src/routes/phases/create.js +++ b/src/routes/phases/create.js @@ -169,7 +169,7 @@ module.exports = [ // So far we don't use the order so it's ok. But in general, we should pass // the original phases. <- TODO _.assign(_.pick(phase.toJSON(), 'id', 'order', 'updatedBy', 'updatedAt'))), - true, // don't send event to Notification Service as the main event here is updating one phase + true, // don't send event to Notification Service as the main event here is updating one phase ); res.status(201).json(newProjectPhase); diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index 106cac7a..af2cdd61 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -438,42 +438,42 @@ describe('Project Phases', () => { it('should send correct BUS API messages when phase added', (done) => { request(server) - .post(`/v5/projects/${projectId}/phases/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ - resource: RESOURCES.PHASE, - name: body.name, - status: body.status, - budget: body.budget, - progress: body.progress, - projectId, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - - done(); - }); - } - }); + .post(`/v5/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ + resource: RESOURCES.PHASE, + name: body.name, + status: body.status, + budget: body.budget, + progress: body.progress, + projectId, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + + done(); + }); + } + }); }); }); @@ -532,30 +532,30 @@ describe('Project Phases', () => { }); sandbox.stub(messageService, 'getClient', () => mockHttpClient); request(server) - .post(`/v5/projects/${projectId}/phases/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - publishSpy.calledOnce.should.be.true; - publishSpy.calledWith('project.phase.added').should.be.true; - createMessageSpy.calledOnce.should.be.true; - createMessageSpy.calledWith(sinon.match({ reference: 'project', - referenceId: '1', - tag: 'phase#1', - title: 'test project phase', - })).should.be.true; - done(); - }); - } - }); + .post(`/v5/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + publishSpy.calledOnce.should.be.true; + publishSpy.calledWith('project.phase.added').should.be.true; + createMessageSpy.calledOnce.should.be.true; + createMessageSpy.calledWith(sinon.match({ reference: 'project', + referenceId: '1', + tag: 'phase#1', + title: 'test project phase', + })).should.be.true; + done(); + }); + } + }); }); }); }); diff --git a/src/routes/phases/delete.js b/src/routes/phases/delete.js index 5d1657bf..ab934ef4 100644 --- a/src/routes/phases/delete.js +++ b/src/routes/phases/delete.js @@ -34,7 +34,7 @@ module.exports = [ } return existing.update({ deletedBy: req.authUser.userId }); }) - .then(entity => entity.destroy())) + .then(entity => entity.destroy())) .then((deleted) => { req.log.debug('deleted project phase', JSON.stringify(deleted, null, 2)); diff --git a/src/routes/phases/delete.spec.js b/src/routes/phases/delete.spec.js index e619ed79..a07842c8 100644 --- a/src/routes/phases/delete.spec.js +++ b/src/routes/phases/delete.spec.js @@ -25,22 +25,22 @@ const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); const expectAfterDelete = (projectId, id, err, next) => { if (err) throw err; setTimeout(() => - models.ProjectPhase.findOne({ - where: { - id, - projectId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); - } - next(); - }), 500); + models.ProjectPhase.findOne({ + where: { + id, + projectId, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); + } + next(); + }), 500); }; const body = { name: 'test project phase', @@ -98,36 +98,36 @@ describe('Project Phases', () => { beforeEach((done) => { // mocks testUtil.clearDb() - .then(() => { - models.Project.create(project).then((p) => { - projectId = p.id; - projectName = p.name; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - _.assign(body, { projectId }); - models.ProjectPhase.create(body).then((phase) => { - phaseId = phase.id; - done(); - }); + .then(() => { + models.Project.create(project).then((p) => { + projectId = p.id; + projectName = p.name; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + _.assign(body, { projectId }); + models.ProjectPhase.create(body).then((phase) => { + phaseId = phase.id; + done(); }); }); }); + }); }); afterEach((done) => { @@ -258,36 +258,36 @@ describe('Project Phases', () => { it('should send correct BUS API messages when phase removed', (done) => { request(server) - .delete(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); + .delete(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); @@ -346,25 +346,25 @@ describe('Project Phases', () => { }); sandbox.stub(messageService, 'getClient', () => mockHttpClient); request(server) - .delete(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - publishSpy.calledOnce.should.be.true; - publishSpy.firstCall.calledWith('project.phase.removed').should.be.true; - deleteTopicSpy.calledOnce.should.be.true; - deleteTopicSpy.calledWith(topic.id).should.be.true; - deletePostsSpy.calledWith(topic.id).should.be.true; - done(); - }); - } - }); + .delete(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + publishSpy.calledOnce.should.be.true; + publishSpy.firstCall.calledWith('project.phase.removed').should.be.true; + deleteTopicSpy.calledOnce.should.be.true; + deleteTopicSpy.calledWith(topic.id).should.be.true; + deletePostsSpy.calledWith(topic.id).should.be.true; + done(); + }); + } + }); }); }); }); diff --git a/src/routes/phases/get.js b/src/routes/phases/get.js index 7ecb77a0..12c1bd3e 100644 --- a/src/routes/phases/get.js +++ b/src/routes/phases/get.js @@ -33,29 +33,29 @@ module.exports = [ }, }, }) - .then((data) => { - if (data.length === 0) { - req.log.debug('No phase found in ES'); - return models.ProjectPhase - .findOne({ - where: { id: phaseId, projectId }, - raw: true, - }) - .then((phase) => { - if (!phase) { + .then((data) => { + if (data.length === 0) { + req.log.debug('No phase found in ES'); + return models.ProjectPhase + .findOne({ + where: { id: phaseId, projectId }, + raw: true, + }) + .then((phase) => { + if (!phase) { // handle 404 - const err = new Error('project phase not found for project id ' + + const err = new Error('project phase not found for project id ' + `${projectId} and phase id ${phaseId}`); - err.status = 404; - throw err; - } - res.json(phase); - }) - .catch(err => next(err)); - } - req.log.debug('phase found in ES'); - return res.json(data[0].inner_hits.phases.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - }) - .catch(next); + err.status = 404; + throw err; + } + res.json(phase); + }) + .catch(err => next(err)); + } + req.log.debug('phase found in ES'); + return res.json(data[0].inner_hits.phases.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + }) + .catch(next); }, ]; diff --git a/src/routes/phases/get.spec.js b/src/routes/phases/get.spec.js index e448f96b..b5d2b1bb 100644 --- a/src/routes/phases/get.spec.js +++ b/src/routes/phases/get.spec.js @@ -42,47 +42,47 @@ describe('Project Phases', () => { before((done) => { // mocks testUtil.clearDb() - .then(() => testUtil.clearES()) - .then(() => { - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => testUtil.clearES()) + .then(() => { + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - projectId = p.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => { - _.assign(body, { projectId }); - models.ProjectPhase.create(body).then((phase) => { - phaseId = phase.id; - done(); - }); + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + _.assign(body, { projectId }); + models.ProjectPhase.create(body).then((phase) => { + phaseId = phase.id; + done(); }); }); }); + }); }); after((done) => { diff --git a/src/routes/phases/update.spec.js b/src/routes/phases/update.spec.js index b8b6910b..b924f396 100644 --- a/src/routes/phases/update.spec.js +++ b/src/routes/phases/update.spec.js @@ -379,338 +379,338 @@ describe('Project Phases', () => { it('should send correct BUS API messages when spentBudget updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - spentBudget: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PAYMENT).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + spentBudget: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PAYMENT).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when progress updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - progress: 50, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PROGRESS).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED).should.be.true; - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + progress: 50, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_PROGRESS).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED).should.be.true; + done(); + }); + } + }); }); it('should send correct BUS API messages when details updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - details: { - text: 'something', - }, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_SCOPE).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + details: { + text: 'something', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_UPDATE_SCOPE).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when status updated (completed)', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - status: 'completed', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + status: 'completed', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when status updated (active)', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId3}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - status: 'active', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId3, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId3}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + status: 'active', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId3, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when budget updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - budget: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + budget: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when startDate updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - startDate: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + startDate: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + + done(); + }); + } + }); }); it('should send correct BUS API messages when duration updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - duration: 100, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - duration: 100, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + duration: 100, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + duration: 100, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + + done(); + }); + } + }); }); it('should send correct BUS API messages when order updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - order: 100, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + order: 100, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; - // NOTE: no other event should be called, as this phase doesn't move any other phases + // NOTE: no other event should be called, as this phase doesn't move any other phases - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when endDate updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .send({ - endDate: new Date(), - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: phaseId, - updatedBy: testUtil.userIds.copilot, - })).should.be.true; + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + endDate: new Date(), + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: phaseId, + updatedBy: testUtil.userIds.copilot, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); @@ -770,29 +770,29 @@ describe('Project Phases', () => { }); sandbox.stub(messageService, 'getClient', () => mockHttpClient); request(server) - .patch(`/v5/projects/${projectId}/phases/${phaseId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(_.assign(updateBody, { budget: 123 })) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - publishSpy.calledOnce.should.be.true; - publishSpy.calledWith('project.phase.updated').should.be.true; - updateMessageSpy.calledOnce.should.be.true; - updateMessageSpy.calledWith(topic.id, sinon.match({ - title: updateBody.name, - postId: topic.posts[0].id, - content: topic.posts[0].body })).should.be.true; - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(_.assign(updateBody, { budget: 123 })) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + publishSpy.calledOnce.should.be.true; + publishSpy.calledWith('project.phase.updated').should.be.true; + updateMessageSpy.calledOnce.should.be.true; + updateMessageSpy.calledWith(topic.id, sinon.match({ + title: updateBody.name, + postId: topic.posts[0].id, + content: topic.posts[0].body })).should.be.true; + done(); + }); + } + }); }); }); }); diff --git a/src/routes/planConfig/revision/create.js b/src/routes/planConfig/revision/create.js index 52c0cb0f..fa815a1a 100644 --- a/src/routes/planConfig/revision/create.js +++ b/src/routes/planConfig/revision/create.js @@ -60,7 +60,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.PLAN_CONFIG_REVISION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/planConfig/revision/create.spec.js b/src/routes/planConfig/revision/create.spec.js index f1a92f83..30aa1bf9 100644 --- a/src/routes/planConfig/revision/create.spec.js +++ b/src/routes/planConfig/revision/create.spec.js @@ -60,10 +60,10 @@ describe('CREATE PlanConfig Revision', () => { it('should return 404 if missing key', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/no-exist-key/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/planConfig/no-exist-key/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -71,10 +71,10 @@ describe('CREATE PlanConfig Revision', () => { it('should return 404 if missing version', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/100/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/planConfig/dev/versions/100/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -97,10 +97,10 @@ describe('CREATE PlanConfig Revision', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -123,7 +123,7 @@ describe('CREATE PlanConfig Revision', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .post('/v5/projects/metadata/planConfig/dev/versions/1/revisions') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) diff --git a/src/routes/planConfig/revision/delete.js b/src/routes/planConfig/revision/delete.js index 8728e000..de3ea456 100644 --- a/src/routes/planConfig/revision/delete.js +++ b/src/routes/planConfig/revision/delete.js @@ -32,24 +32,24 @@ module.exports = [ revision: req.params.revision, }, }).then((planConfig) => { - if (!planConfig) { - const apiErr = new Error('PlanConfig not found for key' + + if (!planConfig) { + const apiErr = new Error('PlanConfig not found for key' + ` ${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return planConfig.update({ - deletedBy: req.authUser.userId, - }); - }).then(planConfig => - planConfig.destroy(), - ).then((planConfig) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PLAN_CONFIG_REVISION, - _.pick(planConfig.toJSON(), 'id')); - res.status(204).end(); - }) + apiErr.status = 404; + return Promise.reject(apiErr); + } + return planConfig.update({ + deletedBy: req.authUser.userId, + }); + }).then(planConfig => + planConfig.destroy(), + ).then((planConfig) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PLAN_CONFIG_REVISION, + _.pick(planConfig.toJSON(), 'id')); + res.status(204).end(); + }) .catch(next)); }, ]; diff --git a/src/routes/planConfig/revision/delete.spec.js b/src/routes/planConfig/revision/delete.spec.js index 1d33284f..36222539 100644 --- a/src/routes/planConfig/revision/delete.spec.js +++ b/src/routes/planConfig/revision/delete.spec.js @@ -10,29 +10,29 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.PlanConfig.findOne({ - where: { - key: 'dev', - version: 1, - revision: 1, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); - - request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + models.PlanConfig.findOne({ + where: { + key: 'dev', + version: 1, + revision: 1, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); + + request(server) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; @@ -79,34 +79,34 @@ describe('DELETE planConfig revision', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403, done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403, done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403, done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/no-existed-key/versions/1/revisions/1') + .delete('/v5/projects/metadata/planConfig/no-existed-key/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -115,7 +115,7 @@ describe('DELETE planConfig revision', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/100/revisions/1') + .delete('/v5/projects/metadata/planConfig/dev/versions/100/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -125,7 +125,7 @@ describe('DELETE planConfig revision', () => { it('should return 404 for non-existed revision', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/100') + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/100') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -134,17 +134,17 @@ describe('DELETE planConfig revision', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') + .delete('/v5/projects/metadata/planConfig/dev/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/planConfig/revision/get.js b/src/routes/planConfig/revision/get.js index a038380d..3bd5563f 100644 --- a/src/routes/planConfig/revision/get.js +++ b/src/routes/planConfig/revision/get.js @@ -43,35 +43,35 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No plan config found in ES'); - models.PlanConfig.findOne({ - where: { - key: req.params.key, - version: req.params.version, - revision: req.params.revision, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((planConfig) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No plan config found in ES'); + models.PlanConfig.findOne({ + where: { + key: req.params.key, + version: req.params.version, + revision: req.params.revision, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((planConfig) => { // Not found - if (!planConfig) { - const apiErr = new Error('PlanConfig not found for key' + + if (!planConfig) { + const apiErr = new Error('PlanConfig not found for key' + `${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(planConfig); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('plan config found in ES'); - res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(planConfig); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('plan config found in ES'); + res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/planConfig/revision/get.spec.js b/src/routes/planConfig/revision/get.spec.js index a524b5da..d69322b8 100644 --- a/src/routes/planConfig/revision/get.spec.js +++ b/src/routes/planConfig/revision/get.spec.js @@ -70,45 +70,45 @@ describe('GET a particular revision of specific version PlanConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') - .expect(403, done); + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/planConfig/revision/list.js b/src/routes/planConfig/revision/list.js index dc18059e..ffb0652c 100644 --- a/src/routes/planConfig/revision/list.js +++ b/src/routes/planConfig/revision/list.js @@ -21,32 +21,34 @@ module.exports = [ permissions('planConfig.view'), (req, res, next) => - util.fetchFromES('planConfigs') - .then((data) => { - if (data.planConfigs.length === 0) { - req.log.debug('No planConfig found in ES'); - models.PlanConfig.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((planConfigs) => { - // Not found - if ((!planConfigs) || (planConfigs.length === 0)) { - const apiErr = new Error(`PlanConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + util.fetchFromES('planConfigs') + .then((data) => { + if (data.planConfigs.length === 0) { + req.log.debug('No planConfig found in ES'); + models.PlanConfig.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((planConfigs) => { + // Not found + if ((!planConfigs) || (planConfigs.length === 0)) { + const apiErr = new Error( + `PlanConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(planConfigs); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('planConfigs found in ES'); - res.json(data.planConfigs); - } - }).catch(next), + res.json(planConfigs); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('planConfigs found in ES'); + res.json(data.planConfigs); + } + }).catch(next), ]; diff --git a/src/routes/planConfig/revision/list.spec.js b/src/routes/planConfig/revision/list.spec.js index 38201af8..8035cd4f 100644 --- a/src/routes/planConfig/revision/list.spec.js +++ b/src/routes/planConfig/revision/list.spec.js @@ -72,45 +72,45 @@ describe('LIST planConfig revisions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .expect(403, done); + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/planConfig/version/create.js b/src/routes/planConfig/version/create.js index dc0526b1..7edd979a 100644 --- a/src/routes/planConfig/version/create.js +++ b/src/routes/planConfig/version/create.js @@ -58,7 +58,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.PLAN_CONFIG_VERSION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/planConfig/version/create.spec.js b/src/routes/planConfig/version/create.spec.js index d3c19562..2d5d09f0 100644 --- a/src/routes/planConfig/version/create.spec.js +++ b/src/routes/planConfig/version/create.spec.js @@ -65,10 +65,10 @@ describe('CREATE PlanConfig version', () => { }); request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/planConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400) @@ -77,10 +77,10 @@ describe('CREATE PlanConfig version', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/planConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -103,10 +103,10 @@ describe('CREATE PlanConfig version', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/planConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .post('/v5/projects/metadata/planConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403) .end(done); diff --git a/src/routes/planConfig/version/delete.js b/src/routes/planConfig/version/delete.js index 493fbdec..1f56a720 100644 --- a/src/routes/planConfig/version/delete.js +++ b/src/routes/planConfig/version/delete.js @@ -29,43 +29,43 @@ module.exports = [ version: req.params.version, }, }).then((allRevision) => { - if (allRevision.length === 0) { - const apiErr = new Error(`PlanConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return models.PlanConfig.update( - { - deletedBy: req.authUser.userId, - }, { - where: { - key: req.params.key, - version: req.params.version, - }, - }); - }) - .then(() => models.PlanConfig.destroy({ - where: { - key: req.params.key, - version: req.params.version, - }, - })) - .then(deleted => models.PlanConfig.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - paranoid: false, - order: [['deletedAt', 'DESC']], - limit: deleted, - })) - .then((planConfigs) => { - _.map(planConfigs, planConfig => util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PLAN_CONFIG_VERSION, - _.pick(planConfig.toJSON(), 'id'))); - res.status(204).end(); + if (allRevision.length === 0) { + const apiErr = new Error(`PlanConfig not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } + return models.PlanConfig.update( + { + deletedBy: req.authUser.userId, + }, { + where: { + key: req.params.key, + version: req.params.version, + }, + }); }) - .catch(next)); + .then(() => models.PlanConfig.destroy({ + where: { + key: req.params.key, + version: req.params.version, + }, + })) + .then(deleted => models.PlanConfig.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + paranoid: false, + order: [['deletedAt', 'DESC']], + limit: deleted, + })) + .then((planConfigs) => { + _.map(planConfigs, planConfig => util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PLAN_CONFIG_VERSION, + _.pick(planConfig.toJSON(), 'id'))); + res.status(204).end(); + }) + .catch(next)); }, ]; diff --git a/src/routes/planConfig/version/delete.spec.js b/src/routes/planConfig/version/delete.spec.js index 79097e62..8b8521e7 100644 --- a/src/routes/planConfig/version/delete.spec.js +++ b/src/routes/planConfig/version/delete.spec.js @@ -10,25 +10,25 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.PlanConfig.findAll({ - where: { - key: 'dev', - version: 1, - }, - paranoid: false, - }) - .then((planConfigs) => { - if (planConfigs.length === 0) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(planConfigs[0].deletedAt); - chai.assert.isNotNull(planConfigs[0].deletedBy); + models.PlanConfig.findAll({ + where: { + key: 'dev', + version: 1, + }, + paranoid: false, + }) + .then((planConfigs) => { + if (planConfigs.length === 0) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(planConfigs[0].deletedAt); + chai.assert.isNotNull(planConfigs[0].deletedBy); - chai.assert.isNotNull(planConfigs[1].deletedAt); - chai.assert.isNotNull(planConfigs[1].deletedBy); - next(); - } - }), 500); + chai.assert.isNotNull(planConfigs[1].deletedAt); + chai.assert.isNotNull(planConfigs[1].deletedBy); + next(); + } + }), 500); }; @@ -76,37 +76,37 @@ describe('DELETE planConfig version', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403) .end(done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403) .end(done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403) .end(done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev111/versions/1') + .delete('/v5/projects/metadata/planConfig/dev111/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -116,7 +116,7 @@ describe('DELETE planConfig version', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/111') + .delete('/v5/projects/metadata/planConfig/dev/versions/111') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -126,17 +126,17 @@ describe('DELETE planConfig version', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/planConfig/dev/versions/1') + .delete('/v5/projects/metadata/planConfig/dev/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/planConfig/version/get.js b/src/routes/planConfig/version/get.js index 46fe1c7d..45274c0a 100644 --- a/src/routes/planConfig/version/get.js +++ b/src/routes/planConfig/version/get.js @@ -41,26 +41,28 @@ module.exports = [ }, sort: { 'planConfigs.version': 'desc' }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No planConfig found in ES'); - models.PlanConfig.latestRevisionOfLatestVersion(req.params.key) - .then((planConfig) => { - if (planConfig == null) { - const apiErr = new Error(`PlanConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(planConfig); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('planConfigs found in ES'); - res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + .then((data) => { + if (data.length === 0) { + req.log.debug('No planConfig found in ES'); + models.PlanConfig.latestRevisionOfLatestVersion(req.params.key) + .then((planConfig) => { + if (planConfig == null) { + const apiErr = new Error( + `PlanConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(planConfig); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('planConfigs found in ES'); + res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, diff --git a/src/routes/planConfig/version/get.spec.js b/src/routes/planConfig/version/get.spec.js index a7455791..6bc1f78d 100644 --- a/src/routes/planConfig/version/get.spec.js +++ b/src/routes/planConfig/version/get.spec.js @@ -91,47 +91,47 @@ describe('GET a latest version of specific key of PlanConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .expect(403) - .end(done); + .get('/v5/projects/metadata/planConfig/dev') + .expect(403) + .end(done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/planConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/planConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/planConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200) .end(done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/planConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200) .end(done); }); diff --git a/src/routes/planConfig/version/getVersion.js b/src/routes/planConfig/version/getVersion.js index 7673c3cf..64db138d 100644 --- a/src/routes/planConfig/version/getVersion.js +++ b/src/routes/planConfig/version/getVersion.js @@ -20,46 +20,48 @@ module.exports = [ validate(schema), permissions('planConfig.view'), (req, res, next) => - util.fetchByIdFromES('planConfigs', { - query: { - nested: { - path: 'planConfigs', - query: { - filtered: { - filter: { - bool: { - must: [ - { term: { 'planConfigs.key': req.params.key } }, - { term: { 'planConfigs.version': req.params.version } }, - ], + util.fetchByIdFromES('planConfigs', { + query: { + nested: { + path: 'planConfigs', + query: { + filtered: { + filter: { + bool: { + must: [ + { term: { 'planConfigs.key': req.params.key } }, + { term: { 'planConfigs.version': req.params.version } }, + ], + }, }, }, }, + inner_hits: {}, }, - inner_hits: {}, }, - }, - sort: { 'planConfigs.revision': 'desc' }, - }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No planConfig found in ES'); - return models.PlanConfig.findOneWithLatestRevision(req.params) - .then((planConfig) => { - // Not found - if (!planConfig) { - const apiErr = new Error(`PlanConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(planConfig); - return Promise.resolve(); - }) - .catch(next); - } - req.log.debug('planConfigs found in ES'); - res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - return Promise.resolve(); - }) - .catch(next), + sort: { 'planConfigs.revision': 'desc' }, + }, 'metadata') + .then((data) => { + if (data.length === 0) { + req.log.debug('No planConfig found in ES'); + return models.PlanConfig.findOneWithLatestRevision(req.params) + .then((planConfig) => { + // Not found + if (!planConfig) { + const apiErr = new Error( + `PlanConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(planConfig); + return Promise.resolve(); + }) + .catch(next); + } + req.log.debug('planConfigs found in ES'); + res.json(data[0].inner_hits.planConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + return Promise.resolve(); + }) + .catch(next), ]; diff --git a/src/routes/planConfig/version/getVersion.spec.js b/src/routes/planConfig/version/getVersion.spec.js index 3052a224..f97a0c60 100644 --- a/src/routes/planConfig/version/getVersion.spec.js +++ b/src/routes/planConfig/version/getVersion.spec.js @@ -81,45 +81,45 @@ describe('GET a particular version of specific key of PlanConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1') - .expect(403, done); + .get('/v5/projects/metadata/planConfig/dev/versions/1') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/planConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/planConfig/version/list.js b/src/routes/planConfig/version/list.js index 66c857d9..6358ffd5 100644 --- a/src/routes/planConfig/version/list.js +++ b/src/routes/planConfig/version/list.js @@ -19,41 +19,41 @@ module.exports = [ validate(schema), permissions('planConfig.view'), (req, res, next) => - util.fetchFromES('planConfigs') - .then((data) => { - if (data.planConfigs.length === 0) { - req.log.debug('No planConfig found in ES'); - models.PlanConfig.findAll({ - where: { - key: req.params.key, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((planConfigs) => { - // Not found - if ((!planConfigs) || (planConfigs.length === 0)) { - const apiErr = new Error(`PlanConfig not found for key ${req.params.key}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + util.fetchFromES('planConfigs') + .then((data) => { + if (data.planConfigs.length === 0) { + req.log.debug('No planConfig found in ES'); + models.PlanConfig.findAll({ + where: { + key: req.params.key, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((planConfigs) => { + // Not found + if ((!planConfigs) || (planConfigs.length === 0)) { + const apiErr = new Error(`PlanConfig not found for key ${req.params.key}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - const latestPlanConfigs = {}; - planConfigs.forEach((element) => { - const isNewerRevision = (latestPlanConfigs[element.version] != null) && + const latestPlanConfigs = {}; + planConfigs.forEach((element) => { + const isNewerRevision = (latestPlanConfigs[element.version] != null) && (latestPlanConfigs[element.version].revision < element.revision); - if ((latestPlanConfigs[element.version] == null) || isNewerRevision) { - latestPlanConfigs[element.version] = element; - } - }); - res.json(Object.values(latestPlanConfigs)); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('planConfigs found in ES'); - res.json(data.planConfigs); - } - }).catch(next), + if ((latestPlanConfigs[element.version] == null) || isNewerRevision) { + latestPlanConfigs[element.version] = element; + } + }); + res.json(Object.values(latestPlanConfigs)); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('planConfigs found in ES'); + res.json(data.planConfigs); + } + }).catch(next), ]; diff --git a/src/routes/planConfig/version/list.spec.js b/src/routes/planConfig/version/list.spec.js index 8fc3ce1b..3369feab 100644 --- a/src/routes/planConfig/version/list.spec.js +++ b/src/routes/planConfig/version/list.spec.js @@ -72,45 +72,45 @@ describe('LIST planConfig versions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions') - .expect(403, done); + .get('/v5/projects/metadata/planConfig/dev/versions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/planConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/planConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/planConfig/version/update.js b/src/routes/planConfig/version/update.js index b62b63c9..125ce62a 100644 --- a/src/routes/planConfig/version/update.js +++ b/src/routes/planConfig/version/update.js @@ -51,26 +51,26 @@ module.exports = [ } return Promise.resolve(planConfigs[0]); }) - .then((planConfig) => { - const revision = planConfig.revision + 1; - const entity = { - version: req.params.version, - revision, - createdBy: req.authUser.userId, - updatedBy: req.authUser.userId, - key: req.params.key, - config: req.body.config, - }; - return models.PlanConfig.create(entity); - }) - .then((createdEntity) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, - RESOURCES.PLAN_CONFIG_VERSION, - createdEntity.toJSON()); - // Omit deletedAt, deletedBy - res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); - }) - .catch(next)); + .then((planConfig) => { + const revision = planConfig.revision + 1; + const entity = { + version: req.params.version, + revision, + createdBy: req.authUser.userId, + updatedBy: req.authUser.userId, + key: req.params.key, + config: req.body.config, + }; + return models.PlanConfig.create(entity); + }) + .then((createdEntity) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, + RESOURCES.PLAN_CONFIG_VERSION, + createdEntity.toJSON()); + // Omit deletedAt, deletedBy + res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); + }) + .catch(next)); }, ]; diff --git a/src/routes/planConfig/version/update.spec.js b/src/routes/planConfig/version/update.spec.js index 025b2b08..b8d61d18 100644 --- a/src/routes/planConfig/version/update.spec.js +++ b/src/routes/planConfig/version/update.spec.js @@ -64,10 +64,10 @@ describe('UPDATE PlanConfig version', () => { config: undefined, }); request(server) - .patch('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400) @@ -76,10 +76,10 @@ describe('UPDATE PlanConfig version', () => { it('should return 201 for admin', (done) => { request(server) - .patch('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -102,10 +102,10 @@ describe('UPDATE PlanConfig version', () => { it('should return 403 for member', (done) => { request(server) - .patch('/v5/projects/metadata/planConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .patch('/v5/projects/metadata/planConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403) .end(done); diff --git a/src/routes/priceConfig/revision/create.js b/src/routes/priceConfig/revision/create.js index 294f176f..8a072d05 100644 --- a/src/routes/priceConfig/revision/create.js +++ b/src/routes/priceConfig/revision/create.js @@ -60,7 +60,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.PRICE_CONFIG_REVISION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/priceConfig/revision/create.spec.js b/src/routes/priceConfig/revision/create.spec.js index 537f043f..d8fc5e94 100644 --- a/src/routes/priceConfig/revision/create.spec.js +++ b/src/routes/priceConfig/revision/create.spec.js @@ -61,10 +61,10 @@ describe('CREATE PriceConfig Revision', () => { it('should return 404 if missing key', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/no-exist-key/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/priceConfig/no-exist-key/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -72,10 +72,10 @@ describe('CREATE PriceConfig Revision', () => { it('should return 404 if missing version', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/100/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/priceConfig/dev/versions/100/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(404, done); @@ -98,10 +98,10 @@ describe('CREATE PriceConfig Revision', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -124,7 +124,7 @@ describe('CREATE PriceConfig Revision', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .post('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) diff --git a/src/routes/priceConfig/revision/delete.js b/src/routes/priceConfig/revision/delete.js index a529ba58..a8bc3369 100644 --- a/src/routes/priceConfig/revision/delete.js +++ b/src/routes/priceConfig/revision/delete.js @@ -32,24 +32,24 @@ module.exports = [ revision: req.params.revision, }, }).then((priceConfig) => { - if (!priceConfig) { - const apiErr = new Error('PriceConfig not found for key' + + if (!priceConfig) { + const apiErr = new Error('PriceConfig not found for key' + ` ${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return priceConfig.update({ - deletedBy: req.authUser.userId, - }); - }).then(priceConfig => - priceConfig.destroy(), - ).then((priceConfig) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PRICE_CONFIG_REVISION, - _.pick(priceConfig.toJSON(), 'id')); - res.status(204).end(); - }) + apiErr.status = 404; + return Promise.reject(apiErr); + } + return priceConfig.update({ + deletedBy: req.authUser.userId, + }); + }).then(priceConfig => + priceConfig.destroy(), + ).then((priceConfig) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PRICE_CONFIG_REVISION, + _.pick(priceConfig.toJSON(), 'id')); + res.status(204).end(); + }) .catch(next)); }, ]; diff --git a/src/routes/priceConfig/revision/delete.spec.js b/src/routes/priceConfig/revision/delete.spec.js index 4ab18cda..11be7e4b 100644 --- a/src/routes/priceConfig/revision/delete.spec.js +++ b/src/routes/priceConfig/revision/delete.spec.js @@ -10,29 +10,29 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.PriceConfig.findOne({ - where: { - key: 'dev', - version: 1, - revision: 1, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.PriceConfig.findOne({ + where: { + key: 'dev', + version: 1, + revision: 1, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; @@ -78,34 +78,34 @@ describe('DELETE priceConfig revision', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403, done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403, done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403, done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/no-existed-key/versions/1/revisions/1') + .delete('/v5/projects/metadata/priceConfig/no-existed-key/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -114,7 +114,7 @@ describe('DELETE priceConfig revision', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/100/revisions/1') + .delete('/v5/projects/metadata/priceConfig/dev/versions/100/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -124,7 +124,7 @@ describe('DELETE priceConfig revision', () => { it('should return 404 for non-existed revision', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/100') + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/100') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -133,17 +133,17 @@ describe('DELETE priceConfig revision', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') + .delete('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/priceConfig/revision/get.js b/src/routes/priceConfig/revision/get.js index acd2da9a..aa35afeb 100644 --- a/src/routes/priceConfig/revision/get.js +++ b/src/routes/priceConfig/revision/get.js @@ -43,35 +43,35 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No priceConfi found in ES'); - models.PriceConfig.findOne({ - where: { - key: req.params.key, - version: req.params.version, - revision: req.params.revision, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((priceConfig) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No priceConfi found in ES'); + models.PriceConfig.findOne({ + where: { + key: req.params.key, + version: req.params.version, + revision: req.params.revision, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((priceConfig) => { // Not found - if (!priceConfig) { - const apiErr = new Error('PriceConfig not found for key' + + if (!priceConfig) { + const apiErr = new Error('PriceConfig not found for key' + ` ${req.params.key} version ${req.params.version} revision ${req.params.revision}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(priceConfig); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('priceConfigs found in ES'); - res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(priceConfig); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('priceConfigs found in ES'); + res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/priceConfig/revision/get.spec.js b/src/routes/priceConfig/revision/get.spec.js index 65f6e72b..3d9423cc 100644 --- a/src/routes/priceConfig/revision/get.spec.js +++ b/src/routes/priceConfig/revision/get.spec.js @@ -70,45 +70,45 @@ describe('GET a particular revision of specific version PriceConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') - .expect(403, done); + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions/2') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/priceConfig/revision/list.js b/src/routes/priceConfig/revision/list.js index bbfa404d..21561ef5 100644 --- a/src/routes/priceConfig/revision/list.js +++ b/src/routes/priceConfig/revision/list.js @@ -20,33 +20,35 @@ module.exports = [ validate(schema), permissions('priceConfig.view'), (req, res, next) => - util.fetchFromES('priceConfigs') - .then((data) => { - if (data.priceConfigs.length === 0) { - req.log.debug('No priceConfig found in ES'); - models.PriceConfig.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((priceConfigs) => { - // Not found - if ((!priceConfigs) || (priceConfigs.length === 0)) { - const apiErr = new Error(`PriceConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + util.fetchFromES('priceConfigs') + .then((data) => { + if (data.priceConfigs.length === 0) { + req.log.debug('No priceConfig found in ES'); + models.PriceConfig.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((priceConfigs) => { + // Not found + if ((!priceConfigs) || (priceConfigs.length === 0)) { + const apiErr = new Error( + `PriceConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(priceConfigs); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('priceConfigs found in ES'); - res.json(data.priceConfigs); - } - }).catch(next), + res.json(priceConfigs); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('priceConfigs found in ES'); + res.json(data.priceConfigs); + } + }).catch(next), ]; diff --git a/src/routes/priceConfig/revision/list.spec.js b/src/routes/priceConfig/revision/list.spec.js index 04623fda..76d3c390 100644 --- a/src/routes/priceConfig/revision/list.spec.js +++ b/src/routes/priceConfig/revision/list.spec.js @@ -72,45 +72,45 @@ describe('LIST priceConfig revisions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .expect(403, done); + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1/revisions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/priceConfig/version/create.js b/src/routes/priceConfig/version/create.js index 504e9f7c..ef299596 100644 --- a/src/routes/priceConfig/version/create.js +++ b/src/routes/priceConfig/version/create.js @@ -60,7 +60,7 @@ module.exports = [ EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, RESOURCES.PRICE_CONFIG_VERSION, createdEntity.toJSON()); - // Omit deletedAt, deletedBy + // Omit deletedAt, deletedBy res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); }) .catch(next)); diff --git a/src/routes/priceConfig/version/create.spec.js b/src/routes/priceConfig/version/create.spec.js index 23a659e4..645366a1 100644 --- a/src/routes/priceConfig/version/create.spec.js +++ b/src/routes/priceConfig/version/create.spec.js @@ -64,10 +64,10 @@ describe('CREATE PriceConfig version', () => { }); request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/priceConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400, done); @@ -75,10 +75,10 @@ describe('CREATE PriceConfig version', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/priceConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -101,10 +101,10 @@ describe('CREATE PriceConfig version', () => { it('should return 403 for member', (done) => { request(server) - .post('/v5/projects/metadata/priceConfig/dev/versions/') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .post('/v5/projects/metadata/priceConfig/dev/versions/') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403, done); }); diff --git a/src/routes/priceConfig/version/delete.js b/src/routes/priceConfig/version/delete.js index 3a9eaf28..db3f26f3 100644 --- a/src/routes/priceConfig/version/delete.js +++ b/src/routes/priceConfig/version/delete.js @@ -29,43 +29,43 @@ module.exports = [ version: req.params.version, }, }).then((allRevision) => { - if (allRevision.length === 0) { - const apiErr = new Error(`PriceConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - return models.PriceConfig.update( - { - deletedBy: req.authUser.userId, - }, { - where: { - key: req.params.key, - version: req.params.version, - }, - }); - }) - .then(() => models.PriceConfig.destroy({ - where: { - key: req.params.key, - version: req.params.version, - }, - })) - .then(deleted => models.PriceConfig.findAll({ - where: { - key: req.params.key, - version: req.params.version, - }, - paranoid: false, - order: [['deletedAt', 'DESC']], - limit: deleted, - })) - .then((priceConfigs) => { - _.map(priceConfigs, priceConfig => util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PRICE_CONFIG_VERSION, - _.pick(priceConfig.toJSON(), 'id'))); - res.status(204).end(); + if (allRevision.length === 0) { + const apiErr = new Error(`PriceConfig not found for key ${req.params.key} version ${req.params.version}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } + return models.PriceConfig.update( + { + deletedBy: req.authUser.userId, + }, { + where: { + key: req.params.key, + version: req.params.version, + }, + }); }) - .catch(next)); + .then(() => models.PriceConfig.destroy({ + where: { + key: req.params.key, + version: req.params.version, + }, + })) + .then(deleted => models.PriceConfig.findAll({ + where: { + key: req.params.key, + version: req.params.version, + }, + paranoid: false, + order: [['deletedAt', 'DESC']], + limit: deleted, + })) + .then((priceConfigs) => { + _.map(priceConfigs, priceConfig => util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PRICE_CONFIG_VERSION, + _.pick(priceConfig.toJSON(), 'id'))); + res.status(204).end(); + }) + .catch(next)); }, ]; diff --git a/src/routes/priceConfig/version/delete.spec.js b/src/routes/priceConfig/version/delete.spec.js index 534ffc82..916a2e99 100644 --- a/src/routes/priceConfig/version/delete.spec.js +++ b/src/routes/priceConfig/version/delete.spec.js @@ -10,25 +10,25 @@ import testUtil from '../../../tests/util'; const expectAfterDelete = (err, next) => { if (err) throw err; setTimeout(() => - models.PriceConfig.findAll({ - where: { - key: 'dev', - version: 1, - }, - paranoid: false, - }) - .then((priceConfigs) => { - if (priceConfigs.length === 0) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(priceConfigs[0].deletedAt); - chai.assert.isNotNull(priceConfigs[0].deletedBy); + models.PriceConfig.findAll({ + where: { + key: 'dev', + version: 1, + }, + paranoid: false, + }) + .then((priceConfigs) => { + if (priceConfigs.length === 0) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(priceConfigs[0].deletedAt); + chai.assert.isNotNull(priceConfigs[0].deletedBy); - chai.assert.isNotNull(priceConfigs[1].deletedAt); - chai.assert.isNotNull(priceConfigs[1].deletedBy); - next(); - } - }), 500); + chai.assert.isNotNull(priceConfigs[1].deletedAt); + chai.assert.isNotNull(priceConfigs[1].deletedBy); + next(); + } + }), 500); }; @@ -75,34 +75,34 @@ describe('DELETE priceConfig version', () => { it('should return 403 for member', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(403, done); }); it('should return 403 for copilot', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(403, done); }); it('should return 403 for manager', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(403, done); }); it('should return 404 for non-existed key', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev111/versions/1') + .delete('/v5/projects/metadata/priceConfig/dev111/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -111,7 +111,7 @@ describe('DELETE priceConfig version', () => { it('should return 404 for non-existed version', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/111') + .delete('/v5/projects/metadata/priceConfig/dev/versions/111') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -120,17 +120,17 @@ describe('DELETE priceConfig version', () => { it('should return 204, for admin', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .delete('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .expect(204) .end(err => expectAfterDelete(err, done)); }); it('should return 204, for connect admin', (done) => { request(server) - .delete('/v5/projects/metadata/priceConfig/dev/versions/1') + .delete('/v5/projects/metadata/priceConfig/dev/versions/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/priceConfig/version/get.js b/src/routes/priceConfig/version/get.js index d23b4bae..48444b48 100644 --- a/src/routes/priceConfig/version/get.js +++ b/src/routes/priceConfig/version/get.js @@ -41,26 +41,28 @@ module.exports = [ }, sort: { 'priceConfigs.version': 'desc' }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No priceConfig found in ES'); + .then((data) => { + if (data.length === 0) { + req.log.debug('No priceConfig found in ES'); - models.PriceConfig.latestRevisionOfLatestVersion(req.params.key) - .then((priceConfig) => { - if (priceConfig == null) { - const apiErr = new Error(`PriceConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(priceConfig); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('priceConfigs found in ES'); - res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + models.PriceConfig.latestRevisionOfLatestVersion(req.params.key) + .then((priceConfig) => { + if (priceConfig == null) { + const apiErr = new Error( + `PriceConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(priceConfig); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('priceConfigs found in ES'); + res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/priceConfig/version/get.spec.js b/src/routes/priceConfig/version/get.spec.js index e7062502..4f1c2f76 100644 --- a/src/routes/priceConfig/version/get.spec.js +++ b/src/routes/priceConfig/version/get.spec.js @@ -91,45 +91,45 @@ describe('GET a latest version of specific key of PriceConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .expect(403, done); + .get('/v5/projects/metadata/priceConfig/dev') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/priceConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/priceConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/priceConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/priceConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/priceConfig/version/getVersion.js b/src/routes/priceConfig/version/getVersion.js index c6b9120e..58bddb7d 100644 --- a/src/routes/priceConfig/version/getVersion.js +++ b/src/routes/priceConfig/version/getVersion.js @@ -41,26 +41,28 @@ module.exports = [ }, sort: { 'priceConfigs.revision': 'desc' }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No priceConfig found in ES'); - return models.PriceConfig.findOneWithLatestRevision(req.params) - .then((priceConfig) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No priceConfig found in ES'); + return models.PriceConfig.findOneWithLatestRevision(req.params) + .then((priceConfig) => { // Not found - if (!priceConfig) { - const apiErr = new Error(`PriceConfig not found for key ${req.params.key} version ${req.params.version}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - res.json(priceConfig); - return Promise.resolve(); - }) - .catch(next); - } - req.log.debug('priceConfigs found in ES'); - res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - return Promise.resolve(); - }) - .catch(next); + if (!priceConfig) { + const apiErr = new Error( + `PriceConfig not found for key ${req.params.key} version ${req.params.version}`, + ); + apiErr.status = 404; + return Promise.reject(apiErr); + } + res.json(priceConfig); + return Promise.resolve(); + }) + .catch(next); + } + req.log.debug('priceConfigs found in ES'); + res.json(data[0].inner_hits.priceConfigs.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/priceConfig/version/getVersion.spec.js b/src/routes/priceConfig/version/getVersion.spec.js index 7f693610..0ba1ee75 100644 --- a/src/routes/priceConfig/version/getVersion.spec.js +++ b/src/routes/priceConfig/version/getVersion.spec.js @@ -81,45 +81,45 @@ describe('GET a particular version of specific key of PriceConfig', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1') - .expect(403, done); + .get('/v5/projects/metadata/priceConfig/dev/versions/1') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/priceConfig/dev') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/priceConfig/version/list.js b/src/routes/priceConfig/version/list.js index a8257e94..c2a1f781 100644 --- a/src/routes/priceConfig/version/list.js +++ b/src/routes/priceConfig/version/list.js @@ -19,39 +19,39 @@ module.exports = [ validate(schema), permissions('priceConfig.view'), (req, res, next) => - util.fetchFromES('priceConfigs') - .then((data) => { - if (data.priceConfigs.length === 0) { - req.log.debug('No priceConfig found in ES'); - models.PriceConfig.findAll({ - where: { - key: req.params.key, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((priceConfigs) => { - // Not found - if ((!priceConfigs) || (priceConfigs.length === 0)) { - const apiErr = new Error(`PriceConfig not found for key ${req.params.key}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + util.fetchFromES('priceConfigs') + .then((data) => { + if (data.priceConfigs.length === 0) { + req.log.debug('No priceConfig found in ES'); + models.PriceConfig.findAll({ + where: { + key: req.params.key, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((priceConfigs) => { + // Not found + if ((!priceConfigs) || (priceConfigs.length === 0)) { + const apiErr = new Error(`PriceConfig not found for key ${req.params.key}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - const latestPriceConfigs = {}; - priceConfigs.forEach((element) => { - const isNewerRevision = (latestPriceConfigs[element.version] != null) && + const latestPriceConfigs = {}; + priceConfigs.forEach((element) => { + const isNewerRevision = (latestPriceConfigs[element.version] != null) && (latestPriceConfigs[element.version].revision < element.revision); - if ((latestPriceConfigs[element.version] == null) || isNewerRevision) { - latestPriceConfigs[element.version] = element; - } - }); - res.json(Object.values(latestPriceConfigs)); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('priceConfigs found in ES'); - res.json(data.priceConfigs); - } - }).catch(next), + if ((latestPriceConfigs[element.version] == null) || isNewerRevision) { + latestPriceConfigs[element.version] = element; + } + }); + res.json(Object.values(latestPriceConfigs)); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('priceConfigs found in ES'); + res.json(data.priceConfigs); + } + }).catch(next), ]; diff --git a/src/routes/priceConfig/version/list.spec.js b/src/routes/priceConfig/version/list.spec.js index ec245406..dc950a04 100644 --- a/src/routes/priceConfig/version/list.spec.js +++ b/src/routes/priceConfig/version/list.spec.js @@ -72,45 +72,45 @@ describe('LIST priceConfig versions', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions') - .expect(403, done); + .get('/v5/projects/metadata/priceConfig/dev/versions') + .expect(403, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) .expect(200) .end(done); }); it('should return 200 for connect manager', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) .expect(200) .end(done); }); it('should return 200 for member', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .expect(200, done); }); it('should return 200 for copilot', (done) => { request(server) - .get('/v5/projects/metadata/priceConfig/dev/versions') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) + .get('/v5/projects/metadata/priceConfig/dev/versions') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) .expect(200, done); }); }); diff --git a/src/routes/priceConfig/version/update.js b/src/routes/priceConfig/version/update.js index 1a58138d..63203717 100644 --- a/src/routes/priceConfig/version/update.js +++ b/src/routes/priceConfig/version/update.js @@ -53,26 +53,26 @@ module.exports = [ } return Promise.resolve(priceConfigs[0]); }) - .then((priceConfig) => { - const revision = priceConfig.revision + 1; - const entity = { - version: req.params.version, - revision, - createdBy: req.authUser.userId, - updatedBy: req.authUser.userId, - key: req.params.key, - config: req.body.config, - }; - return models.PriceConfig.create(entity); - }) - .then((createdEntity) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, - RESOURCES.PRICE_CONFIG_VERSION, - createdEntity.toJSON()); - // Omit deletedAt, deletedBy - res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); - }) - .catch(next)); + .then((priceConfig) => { + const revision = priceConfig.revision + 1; + const entity = { + version: req.params.version, + revision, + createdBy: req.authUser.userId, + updatedBy: req.authUser.userId, + key: req.params.key, + config: req.body.config, + }; + return models.PriceConfig.create(entity); + }) + .then((createdEntity) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_CREATE, + RESOURCES.PRICE_CONFIG_VERSION, + createdEntity.toJSON()); + // Omit deletedAt, deletedBy + res.status(201).json(_.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy')); + }) + .catch(next)); }, ]; diff --git a/src/routes/priceConfig/version/update.spec.js b/src/routes/priceConfig/version/update.spec.js index ebb4d853..73a18bc4 100644 --- a/src/routes/priceConfig/version/update.spec.js +++ b/src/routes/priceConfig/version/update.spec.js @@ -63,10 +63,10 @@ describe('UPDATE PriceConfig version', () => { config: undefined, }); request(server) - .patch('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(invalidBody) .expect('Content-Type', /json/) .expect(400, done); @@ -74,10 +74,10 @@ describe('UPDATE PriceConfig version', () => { it('should return 201 for admin', (done) => { request(server) - .patch('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .patch('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect('Content-Type', /json/) .expect(201) @@ -100,10 +100,10 @@ describe('UPDATE PriceConfig version', () => { it('should return 403 for member', (done) => { request(server) - .patch('/v5/projects/metadata/priceConfig/dev/versions/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) + .patch('/v5/projects/metadata/priceConfig/dev/versions/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) .send(body) .expect(403, done); }); diff --git a/src/routes/productCategories/delete.js b/src/routes/productCategories/delete.js index c5a9f084..9e364781 100644 --- a/src/routes/productCategories/delete.js +++ b/src/routes/productCategories/delete.js @@ -21,7 +21,7 @@ module.exports = [ validate(schema), permissions('productCategory.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.ProductCategory.findByPk(req.params.key) .then((entity) => { if (!entity) { @@ -33,12 +33,12 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then((entity) => { - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PRODUCT_CATEGORY, - _.pick(entity.toJSON(), 'key')); - res.status(204).end(); - }) - .catch(next), + .then((entity) => { + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PRODUCT_CATEGORY, + _.pick(entity.toJSON(), 'key')); + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/productCategories/delete.spec.js b/src/routes/productCategories/delete.spec.js index d5ada904..198585ed 100644 --- a/src/routes/productCategories/delete.spec.js +++ b/src/routes/productCategories/delete.spec.js @@ -12,27 +12,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (key, err, next) => { if (err) throw err; setTimeout(() => - models.ProductCategory.findOne({ - where: { - key, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.ProductCategory.findOne({ + where: { + key, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/metadata/productCategories/${key}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/metadata/productCategories/${key}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE product category', () => { diff --git a/src/routes/productCategories/get.js b/src/routes/productCategories/get.js index 8279dbc9..728e41fb 100644 --- a/src/routes/productCategories/get.js +++ b/src/routes/productCategories/get.js @@ -30,32 +30,32 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No productCategory found in ES'); - models.ProductCategory.findOne({ - where: { - key: req.params.key, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((productCategory) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No productCategory found in ES'); + models.ProductCategory.findOne({ + where: { + key: req.params.key, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((productCategory) => { // Not found - if (!productCategory) { - const apiErr = new Error(`Product category not found for key ${req.params.key}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!productCategory) { + const apiErr = new Error(`Product category not found for key ${req.params.key}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(productCategory); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('productCategories found in ES'); - res.json(data[0].inner_hits.productCategories.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(productCategory); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('productCategories found in ES'); + res.json(data[0].inner_hits.productCategories.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/productCategories/list.js b/src/routes/productCategories/list.js index 4a5553c9..bc02e2f8 100644 --- a/src/routes/productCategories/list.js +++ b/src/routes/productCategories/list.js @@ -11,21 +11,21 @@ module.exports = [ permissions('productCategory.view'), (req, res, next) => { util.fetchFromES('productCategories') - .then((data) => { - if (data.productCategories.length === 0) { - req.log.debug('No productCategory found in ES'); - models.ProductCategory.findAll({ - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((productCategories) => { - res.json(productCategories); + .then((data) => { + if (data.productCategories.length === 0) { + req.log.debug('No productCategory found in ES'); + models.ProductCategory.findAll({ + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, }) - .catch(next); - } else { - req.log.debug('productCategories found in ES'); - res.json(data.productCategories); - } - }); + .then((productCategories) => { + res.json(productCategories); + }) + .catch(next); + } else { + req.log.debug('productCategories found in ES'); + res.json(data.productCategories); + } + }); }, ]; diff --git a/src/routes/productTemplates/create.js b/src/routes/productTemplates/create.js index 0dfcc4d1..978aeb74 100644 --- a/src/routes/productTemplates/create.js +++ b/src/routes/productTemplates/create.js @@ -38,8 +38,8 @@ const schema = { updatedBy: Joi.any().strip(), deletedBy: Joi.any().strip(), }) - .xor('form', 'template') - .required(), + .xor('form', 'template') + .required(), }; diff --git a/src/routes/productTemplates/delete.js b/src/routes/productTemplates/delete.js index f702a33b..b6c23e2f 100644 --- a/src/routes/productTemplates/delete.js +++ b/src/routes/productTemplates/delete.js @@ -21,7 +21,7 @@ module.exports = [ validate(schema), permissions('productTemplate.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.ProductTemplate.findByPk(req.params.templateId) .then((entity) => { if (!entity) { @@ -33,13 +33,13 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then((entity) => { - // emit event - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PRODUCT_TEMPLATE, - _.pick(entity.toJSON(), 'id')); - res.status(204).end(); - }) - .catch(next), + .then((entity) => { + // emit event + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PRODUCT_TEMPLATE, + _.pick(entity.toJSON(), 'id')); + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/productTemplates/delete.spec.js b/src/routes/productTemplates/delete.spec.js index 893653eb..c9090a2c 100644 --- a/src/routes/productTemplates/delete.spec.js +++ b/src/routes/productTemplates/delete.spec.js @@ -11,27 +11,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (id, err, next) => { if (err) throw err; setTimeout(() => - models.ProductTemplate.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.ProductTemplate.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/metadata/productTemplates/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/metadata/productTemplates/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; diff --git a/src/routes/productTemplates/get.js b/src/routes/productTemplates/get.js index 96780c2b..6ed71f62 100644 --- a/src/routes/productTemplates/get.js +++ b/src/routes/productTemplates/get.js @@ -30,34 +30,34 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No productTemplate found in ES'); - models.ProductTemplate.findOne({ - where: { - deletedAt: { $eq: null }, - id: req.params.templateId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((productTemplate) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No productTemplate found in ES'); + models.ProductTemplate.findOne({ + where: { + deletedAt: { $eq: null }, + id: req.params.templateId, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then((productTemplate) => { // Not found - if (!productTemplate) { - const apiErr = new Error(`Product template not found for product id ${req.params.templateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!productTemplate) { + const apiErr = new Error(`Product template not found for product id ${req.params.templateId}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(productTemplate); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('productTemplates found in ES'); - res.json(data[0].inner_hits.productTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(productTemplate); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('productTemplates found in ES'); + res.json(data[0].inner_hits.productTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/productTemplates/list.js b/src/routes/productTemplates/list.js index f1816d86..ad54be40 100644 --- a/src/routes/productTemplates/list.js +++ b/src/routes/productTemplates/list.js @@ -11,30 +11,30 @@ module.exports = [ permissions('productTemplate.view'), (req, res, next) => { util.fetchFromES('productTemplates') - .then((data) => { - const filters = req.query; - if (!util.isValidFilter(filters, ['productKey'])) { - util.handleError('Invalid filters', null, req, next); - } - const where = { deletedAt: { $eq: null }, disabled: false }; - if (filters.productKey) { - where.productKey = { $eq: filters.productKey }; - } - if (data.productTemplates.length === 0) { - req.log.debug('No productTemplate found in ES'); - models.ProductTemplate.findAll({ - where, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((productTemplates) => { - res.json(productTemplates); - }) - .catch(next); - } else { - req.log.debug('productTemplates found in ES'); - res.json(data.productTemplates); - } - }); + .then((data) => { + const filters = req.query; + if (!util.isValidFilter(filters, ['productKey'])) { + util.handleError('Invalid filters', null, req, next); + } + const where = { deletedAt: { $eq: null }, disabled: false }; + if (filters.productKey) { + where.productKey = { $eq: filters.productKey }; + } + if (data.productTemplates.length === 0) { + req.log.debug('No productTemplate found in ES'); + models.ProductTemplate.findAll({ + where, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then((productTemplates) => { + res.json(productTemplates); + }) + .catch(next); + } else { + req.log.debug('productTemplates found in ES'); + res.json(data.productTemplates); + } + }); }, ]; diff --git a/src/routes/productTemplates/list.spec.js b/src/routes/productTemplates/list.spec.js index 09af4696..788af5bf 100644 --- a/src/routes/productTemplates/list.spec.js +++ b/src/routes/productTemplates/list.spec.js @@ -16,7 +16,9 @@ const validateProductTemplates = (count, resJson, expectedTemplates) => { resJson.forEach((pt, idx) => { pt.should.include.all.keys('id', 'name', 'productKey', 'category', 'subCategory', 'icon', 'brief', 'details', - 'aliases', 'template', 'disabled', 'form', 'hidden', 'isAddOn', 'createdBy', 'createdAt', 'updatedBy', 'updatedAt'); + 'aliases', 'template', 'disabled', 'form', 'hidden', 'isAddOn', 'createdBy', 'createdAt', 'updatedBy', + 'updatedAt', + ); pt.should.not.include.all.keys('deletedAt', 'deletedBy'); pt.name.should.be.eql(expectedTemplates[idx].name); pt.productKey.should.be.eql(expectedTemplates[idx].productKey); @@ -93,11 +95,11 @@ describe('LIST product templates', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductTemplate.create(templates[0])) - .then((createdTemplate) => { - templateId = createdTemplate.id; - return models.ProductTemplate.create(templates[1]).then(() => done()); - }); + .then(() => models.ProductTemplate.create(templates[0])) + .then((createdTemplate) => { + templateId = createdTemplate.id; + return models.ProductTemplate.create(templates[1]).then(() => done()); + }); }); after((done) => { testUtil.clearDb(done); diff --git a/src/routes/productTemplates/update.js b/src/routes/productTemplates/update.js index a96c4c31..44a3d1e8 100644 --- a/src/routes/productTemplates/update.js +++ b/src/routes/productTemplates/update.js @@ -41,8 +41,8 @@ const schema = { updatedBy: Joi.any().strip(), deletedBy: Joi.any().strip(), }) - .xor('form', 'template') - .required(), + .xor('form', 'template') + .required(), }; module.exports = [ diff --git a/src/routes/productTemplates/update.spec.js b/src/routes/productTemplates/update.spec.js index 3526b1cd..bd11e9d6 100644 --- a/src/routes/productTemplates/update.spec.js +++ b/src/routes/productTemplates/update.spec.js @@ -71,34 +71,34 @@ describe('UPDATE product template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.Form.create(forms[0])) - .then(() => models.Form.create(forms[1])) - .then(() => models.ProductCategory.bulkCreate([ - { - key: 'generic', - displayName: 'Generic', - icon: 'http://example.com/icon1.ico', - question: 'question 1', - info: 'info 1', - aliases: ['key-1', 'key_1'], - createdBy: 1, - updatedBy: 1, - }, - { - key: 'concrete', - displayName: 'Concrete', - icon: 'http://example.com/icon1.ico', - question: 'question 2', - info: 'info 2', - aliases: ['key-2', 'key_2'], - createdBy: 1, - updatedBy: 1, - }, - ])) - .then(() => models.ProductTemplate.create(template).then((createdTemplate) => { - templateId = createdTemplate.id; - done(); - })); + .then(() => models.Form.create(forms[0])) + .then(() => models.Form.create(forms[1])) + .then(() => models.ProductCategory.bulkCreate([ + { + key: 'generic', + displayName: 'Generic', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + createdBy: 1, + updatedBy: 1, + }, + { + key: 'concrete', + displayName: 'Concrete', + icon: 'http://example.com/icon1.ico', + question: 'question 2', + info: 'info 2', + aliases: ['key-2', 'key_2'], + createdBy: 1, + updatedBy: 1, + }, + ])) + .then(() => models.ProductTemplate.create(template).then((createdTemplate) => { + templateId = createdTemplate.id; + done(); + })); }); after((done) => { testUtil.clearDb(done); diff --git a/src/routes/productTemplates/upgrade.spec.js b/src/routes/productTemplates/upgrade.spec.js index 9d39c696..407fc100 100644 --- a/src/routes/productTemplates/upgrade.spec.js +++ b/src/routes/productTemplates/upgrade.spec.js @@ -62,71 +62,71 @@ describe('UPGRADE product template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProductCategory.bulkCreate([ - { - key: 'generic', - displayName: 'Generic', - icon: 'http://example.com/icon1.ico', - question: 'question 1', - info: 'info 1', - aliases: ['key-1', 'key_1'], - createdBy: 1, - updatedBy: 1, - }, - { - key: 'concrete', - displayName: 'Concrete', - icon: 'http://example.com/icon1.ico', - question: 'question 2', - info: 'info 2', - aliases: ['key-2', 'key_2'], - createdBy: 1, - updatedBy: 1, - }, - ])) - .then(() => { - const config = { - sections: [{ - id: 'appDefinition', - title: 'Sample Project', - required: true, - description: 'Please answer a few basic questions', - subSections: [{ - id: 'projectName', - required: true, - validationError: 'Please provide a name for your project', - fieldName: 'name', - description: '', - title: 'Project Name', - type: 'project-name', - }, { - id: 'notes', - fieldName: 'details.appDefinition.notes', - title: 'Notes', - description: 'Add any other important information', - type: 'notes', - }], - }], - }; - models.Form.bulkCreate([ + .then(() => models.ProductCategory.bulkCreate([ { - key: 'newKey', - version: 1, - revision: 1, - config, + key: 'generic', + displayName: 'Generic', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + createdBy: 1, + updatedBy: 1, + }, + { + key: 'concrete', + displayName: 'Concrete', + icon: 'http://example.com/icon1.ico', + question: 'question 2', + info: 'info 2', + aliases: ['key-2', 'key_2'], createdBy: 1, updatedBy: 1, }, - ]); - }) - .then(() => models.ProductTemplate.create(productTemplate)) - .then((createdTemplate) => { - templateId = createdTemplate.id; - }) - .then(() => models.ProductTemplate.create(productTemplateMissed).then((createdTemplate) => { - missingTemplateId = createdTemplate.id; - done(); - })); + ])) + .then(() => { + const config = { + sections: [{ + id: 'appDefinition', + title: 'Sample Project', + required: true, + description: 'Please answer a few basic questions', + subSections: [{ + id: 'projectName', + required: true, + validationError: 'Please provide a name for your project', + fieldName: 'name', + description: '', + title: 'Project Name', + type: 'project-name', + }, { + id: 'notes', + fieldName: 'details.appDefinition.notes', + title: 'Notes', + description: 'Add any other important information', + type: 'notes', + }], + }], + }; + models.Form.bulkCreate([ + { + key: 'newKey', + version: 1, + revision: 1, + config, + createdBy: 1, + updatedBy: 1, + }, + ]); + }) + .then(() => models.ProductTemplate.create(productTemplate)) + .then((createdTemplate) => { + templateId = createdTemplate.id; + }) + .then(() => models.ProductTemplate.create(productTemplateMissed).then((createdTemplate) => { + missingTemplateId = createdTemplate.id; + done(); + })); }); after((done) => { testUtil.clearDb(done); diff --git a/src/routes/projectMemberInvites/create.js b/src/routes/projectMemberInvites/create.js index d35ded1f..60b1c570 100644 --- a/src/routes/projectMemberInvites/create.js +++ b/src/routes/projectMemberInvites/create.js @@ -287,142 +287,142 @@ module.exports = [ // we have to filter users returned by the Member Service so we only invite the users // whom we are inviting, because Member Service has a loose search logic and may return // users with handles whom we didn't search for - .then(foundUsers => foundUsers.filter(foundUser => _.includes(invite.handles, foundUser.handle))) - .then((inviteUsers) => { - const members = req.context.currentProjectMembers; - const projectId = _.parseInt(req.params.projectId); - // check user handle exists in returned result - const errorMessageHandleNotExist = 'User with such handle does not exist'; - if (!!invite.handles && invite.handles.length > 0) { - const existentHandles = _.map(inviteUsers, 'handle'); - failed = _.concat(failed, _.map(_.difference(invite.handles, existentHandles), handle => _.assign({}, { - handle, - message: errorMessageHandleNotExist, - }))); - } + .then(foundUsers => foundUsers.filter(foundUser => _.includes(invite.handles, foundUser.handle))) + .then((inviteUsers) => { + const members = req.context.currentProjectMembers; + const projectId = _.parseInt(req.params.projectId); + // check user handle exists in returned result + const errorMessageHandleNotExist = 'User with such handle does not exist'; + if (!!invite.handles && invite.handles.length > 0) { + const existentHandles = _.map(inviteUsers, 'handle'); + failed = _.concat(failed, _.map(_.difference(invite.handles, existentHandles), handle => _.assign({}, { + handle, + message: errorMessageHandleNotExist, + }))); + } - let inviteUserIds = _.map(inviteUsers, 'userId'); - const promises = []; - const errorMessageForAlreadyMemberUser = 'User with such handle is already a member of the team.'; + let inviteUserIds = _.map(inviteUsers, 'userId'); + const promises = []; + const errorMessageForAlreadyMemberUser = 'User with such handle is already a member of the team.'; - if (inviteUserIds) { + if (inviteUserIds) { // remove members already in the team - _.remove(inviteUserIds, u => _.some(members, (m) => { - const isPresent = m.userId === u; - if (isPresent) { - failed.push(_.assign({}, { - handle: getUserHandleById(m.userId, inviteUsers), - message: errorMessageForAlreadyMemberUser, - })); + _.remove(inviteUserIds, u => _.some(members, (m) => { + const isPresent = m.userId === u; + if (isPresent) { + failed.push(_.assign({}, { + handle: getUserHandleById(m.userId, inviteUsers), + message: errorMessageForAlreadyMemberUser, + })); + } + return isPresent; + })); + + // for each user invited by `handle` (userId) we have to load they Topcoder Roles, + // so we can check if such a user can be invited with desired Project Role + // for customers we don't check it to avoid extra call, as any Topcoder user can be invited as customer + if (invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { + _.forEach(inviteUserIds, (userId) => { + req.log.info(userId); + promises.push(util.getUserRoles(userId, req.log, req.id)); + }); } - return isPresent; - })); - - // for each user invited by `handle` (userId) we have to load they Topcoder Roles, - // so we can check if such a user can be invited with desired Project Role - // for customers we don't check it to avoid extra call, as any Topcoder user can be invited as customer - if (invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { - _.forEach(inviteUserIds, (userId) => { - req.log.info(userId); - promises.push(util.getUserRoles(userId, req.log, req.id)); - }); } - } - if (invite.emails) { + if (invite.emails) { // email invites can only be used for CUSTOMER role - if (invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { // eslint-disable-line no-lonely-if - const message = `Emails can only be used for ${PROJECT_MEMBER_ROLE.CUSTOMER}`; - failed = _.concat(failed, _.map(invite.emails, email => _.assign({}, { email, message }))); - delete invite.emails; + if (invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { // eslint-disable-line no-lonely-if + const message = `Emails can only be used for ${PROJECT_MEMBER_ROLE.CUSTOMER}`; + failed = _.concat(failed, _.map(invite.emails, email => _.assign({}, { email, message }))); + delete invite.emails; + } } - } - if (promises.length === 0) { - promises.push(Promise.resolve()); - } - return Promise.all(promises).then((rolesList) => { - if (inviteUserIds && invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { - req.log.debug('Checking if users are allowed to be invited with desired Project Role.'); - const forbidUserList = []; - _.zip(inviteUserIds, rolesList).forEach((data) => { - const [userId, roles] = data; - - if (roles) { - req.log.debug(`Got user (id: ${userId}) Topcoder roles: ${roles.join(', ')}.`); - - if (!util.hasPermission({ topcoderRoles: PROJECT_TO_TOPCODER_ROLES_MATRIX[invite.role] }, { roles })) { + if (promises.length === 0) { + promises.push(Promise.resolve()); + } + return Promise.all(promises).then((rolesList) => { + if (inviteUserIds && invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { + req.log.debug('Checking if users are allowed to be invited with desired Project Role.'); + const forbidUserList = []; + _.zip(inviteUserIds, rolesList).forEach((data) => { + const [userId, roles] = data; + + if (roles) { + req.log.debug(`Got user (id: ${userId}) Topcoder roles: ${roles.join(', ')}.`); + + if (!util.hasPermission({ topcoderRoles: PROJECT_TO_TOPCODER_ROLES_MATRIX[invite.role] }, { roles })) { + forbidUserList.push(userId); + } + } else { + req.log.debug(`Didn't get any Topcoder roles for user (id: ${userId}).`); forbidUserList.push(userId); } - } else { - req.log.debug(`Didn't get any Topcoder roles for user (id: ${userId}).`); - forbidUserList.push(userId); + }); + if (forbidUserList.length > 0) { + const message = `cannot be invited with a "${invite.role}" role to the project`; + failed = _.concat(failed, _.map(forbidUserList, + id => _.assign({}, { handle: getUserHandleById(id, inviteUsers), message }))); + req.log.debug(`Users with id(s) ${forbidUserList.join(', ')} ${message}`); + inviteUserIds = _.filter(inviteUserIds, userId => !_.includes(forbidUserList, userId)); } - }); - if (forbidUserList.length > 0) { - const message = `cannot be invited with a "${invite.role}" role to the project`; - failed = _.concat(failed, _.map(forbidUserList, - id => _.assign({}, { handle: getUserHandleById(id, inviteUsers), message }))); - req.log.debug(`Users with id(s) ${forbidUserList.join(', ')} ${message}`); - inviteUserIds = _.filter(inviteUserIds, userId => !_.includes(forbidUserList, userId)); } - } - return models.ProjectMemberInvite.getPendingInvitesForProject(projectId) - .then((invites) => { - const data = { - projectId, - role: invite.role, - // invite copilots directly if user has permissions - status: (invite.role !== PROJECT_MEMBER_ROLE.COPILOT || + return models.ProjectMemberInvite.getPendingInvitesForProject(projectId) + .then((invites) => { + const data = { + projectId, + role: invite.role, + // invite copilots directly if user has permissions + status: (invite.role !== PROJECT_MEMBER_ROLE.COPILOT || util.hasPermissionByReq(PERMISSION.CREATE_PROJECT_INVITE_COPILOT_DIRECTLY, req)) - ? INVITE_STATUS.PENDING - : INVITE_STATUS.REQUESTED, - createdBy: req.authUser.userId, - updatedBy: req.authUser.userId, - }; - req.log.debug('Creating invites'); - return models.Sequelize.Promise.all(buildCreateInvitePromises( - req, invite.emails, inviteUserIds, invites, data, failed, members, inviteUsers)) - .then((values) => { - values.forEach((v) => { + ? INVITE_STATUS.PENDING + : INVITE_STATUS.REQUESTED, + createdBy: req.authUser.userId, + updatedBy: req.authUser.userId, + }; + req.log.debug('Creating invites'); + return models.Sequelize.Promise.all(buildCreateInvitePromises( + req, invite.emails, inviteUserIds, invites, data, failed, members, inviteUsers)) + .then((values) => { + values.forEach((v) => { // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, - RESOURCES.PROJECT_MEMBER_INVITE, - v.toJSON()); - - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, - v, - { correlationId: req.id }, - ); - // send email invite (async) - if (v.email && !v.userId && v.status === INVITE_STATUS.PENDING) { - sendInviteEmail(req, projectId, v); - } - }); - return values.map(value => value.get({ plain: true })); - }); // models.sequelize.Promise.all - }); // models.ProjectMemberInvite.getPendingInvitesForProject - }) - .then(values => ( + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, + RESOURCES.PROJECT_MEMBER_INVITE, + v.toJSON()); + + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, + v, + { correlationId: req.id }, + ); + // send email invite (async) + if (v.email && !v.userId && v.status === INVITE_STATUS.PENDING) { + sendInviteEmail(req, projectId, v); + } + }); + return values.map(value => value.get({ plain: true })); + }); // models.sequelize.Promise.all + }); // models.ProjectMemberInvite.getPendingInvitesForProject + }) + .then(values => ( // populate successful invites with user details if required - util.getObjectsWithMemberDetails(values, fields, req) - .catch((err) => { - req.log.error('Cannot get user details for invites.'); - req.log.debug('Error during getting user details for invites', err); - // continues without details anyway - return values; - }) - )) - .then((values) => { - const response = _.assign({}, { success: util.postProcessInvites('$[*]', values, req) }); - if (failed.length) { - res.status(403).json(_.assign({}, response, { failed })); - } else { - res.status(201).json(response); - } - }); - }).catch(err => next(err)); + util.getObjectsWithMemberDetails(values, fields, req) + .catch((err) => { + req.log.error('Cannot get user details for invites.'); + req.log.debug('Error during getting user details for invites', err); + // continues without details anyway + return values; + }) + )) + .then((values) => { + const response = _.assign({}, { success: util.postProcessInvites('$[*]', values, req) }); + if (failed.length) { + res.status(403).json(_.assign({}, response, { failed })); + } else { + res.status(201).json(response); + } + }); + }).catch(err => next(err)); }, ]; diff --git a/src/routes/projectMemberInvites/create.spec.js b/src/routes/projectMemberInvites/create.spec.js index 7034a374..370bdb8b 100644 --- a/src/routes/projectMemberInvites/create.spec.js +++ b/src/routes/projectMemberInvites/create.spec.js @@ -217,56 +217,56 @@ describe('Project Member Invite create', () => { }); it('should return 201 if userIds and emails are presented the same time', - (done) => { - request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - handles: ['test_customer1'], - emails: ['hello@world.com'], - role: 'customer', - }) - .expect('Content-Type', /json/) - .expect(201) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body.success[1]; - should.exist(resJson); - resJson.role.should.equal('customer'); - resJson.projectId.should.equal(project1.id); - resJson.email.should.equal('hello@world.com'); - server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true; - done(); - } - }); - }); + (done) => { + request(server) + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + handles: ['test_customer1'], + emails: ['hello@world.com'], + role: 'customer', + }) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.success[1]; + should.exist(resJson); + resJson.role.should.equal('customer'); + resJson.projectId.should.equal(project1.id); + resJson.email.should.equal('hello@world.com'); + server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true; + done(); + } + }); + }); it('should return 400 if neither handles or email is presented', - (done) => { - request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - role: 'customer', - }) - .expect('Content-Type', /json/) - .expect(400) - .end((err, res) => { - if (err) { - done(err); - } else { - const errorMessage = _.get(res.body, 'message', ''); - sinon.assert.match(errorMessage, /.*Either handles or emails are required/); - done(); - } - }); - }); + (done) => { + request(server) + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + role: 'customer', + }) + .expect('Content-Type', /json/) + .expect(400) + .end((err, res) => { + if (err) { + done(err); + } else { + const errorMessage = _.get(res.body, 'message', ''); + sinon.assert.match(errorMessage, /.*Either handles or emails are required/); + done(); + } + }); + }); it('should return 403 if try to create copilot without MANAGER_ROLES', (done) => { const mockHttpClient = _.merge(testUtil.mockHttpClient, { @@ -922,41 +922,41 @@ describe('Project Member Invite create', () => { it('should send correct BUS API messages when invite added by handle', (done) => { request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) - .send({ - handles: ['test_user2'], - role: PROJECT_MEMBER_ROLE.CUSTOMER, - }) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - userId: 40011578, - email: null, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ - projectId: project1.id, - userId: 40011578, - email: null, - isSSO: false, - })).should.be.true; + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .send({ + handles: ['test_user2'], + role: PROJECT_MEMBER_ROLE.CUSTOMER, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - done(); - }); - } - }); + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + userId: 40011578, + email: null, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ + projectId: project1.id, + userId: 40011578, + email: null, + isSSO: false, + })).should.be.true; + + done(); + }); + } + }); }); it('should send correct BUS API messages when invite added by email', (done) => { @@ -970,44 +970,44 @@ describe('Project Member Invite create', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) - .send({ - emails: ['hello@world.com'], - role: PROJECT_MEMBER_ROLE.CUSTOMER, - }) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - userId: null, - email: 'hello@world.com', - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ - projectId: project1.id, - userId: null, - email: 'hello@world.com', - isSSO: false, - })).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_EMAIL_INVITE_CREATED, sinon.match({ - recipients: ['hello@world.com'], - })).should.be.true; + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .send({ + emails: ['hello@world.com'], + role: PROJECT_MEMBER_ROLE.CUSTOMER, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(3); - done(); - }); - } - }); + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + userId: null, + email: 'hello@world.com', + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ + projectId: project1.id, + userId: null, + email: 'hello@world.com', + isSSO: false, + })).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_EMAIL_INVITE_CREATED, sinon.match({ + recipients: ['hello@world.com'], + })).should.be.true; + + done(); + }); + } + }); }); }); }); diff --git a/src/routes/projectMemberInvites/delete.spec.js b/src/routes/projectMemberInvites/delete.spec.js index 05c34564..fbb601ec 100644 --- a/src/routes/projectMemberInvites/delete.spec.js +++ b/src/routes/projectMemberInvites/delete.spec.js @@ -376,29 +376,29 @@ describe('Project member invite delete', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .delete(`/v5/projects/${project1.id}/invites/3`) - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); - - // Events for accepted invite - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REMOVED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - id: 3, - })).should.be.true; - - done(); - }); - } - }); + .delete(`/v5/projects/${project1.id}/invites/3`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); + + // Events for accepted invite + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REMOVED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + id: 3, + })).should.be.true; + + done(); + }); + } + }); }); }); }); diff --git a/src/routes/projectMemberInvites/get.js b/src/routes/projectMemberInvites/get.js index e9b69b6e..23ea0524 100644 --- a/src/routes/projectMemberInvites/get.js +++ b/src/routes/projectMemberInvites/get.js @@ -118,7 +118,7 @@ module.exports = [ return invite; }) )) - .then(invite => res.json(util.postProcessInvites('$.email', invite, req))) - .catch(next); + .then(invite => res.json(util.postProcessInvites('$.email', invite, req))) + .catch(next); }, ]; diff --git a/src/routes/projectMemberInvites/get.spec.js b/src/routes/projectMemberInvites/get.spec.js index 8284ba00..e97b7b3b 100644 --- a/src/routes/projectMemberInvites/get.spec.js +++ b/src/routes/projectMemberInvites/get.spec.js @@ -16,97 +16,97 @@ describe('GET Project Member Invite', () => { // clear ES and db testUtil.clearES().then(() => { testUtil.clearDb() - .then(() => { - const p1 = models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - const pm1 = models.ProjectMember.create({ - userId: testUtil.userIds.admin, - projectId: project1.id, - role: 'copilot', - isPrimary: true, + .then(() => { + const p1 = models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, createdBy: 1, updatedBy: 1, - }); - // create invite - const invite1 = models.ProjectMemberInvite.create({ - id: 1, - userId: testUtil.userIds.member, - email: null, - projectId: project1.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.PENDING, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + const pm1 = models.ProjectMember.create({ + userId: testUtil.userIds.admin, + projectId: project1.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }); + // create invite + const invite1 = models.ProjectMemberInvite.create({ + id: 1, + userId: testUtil.userIds.member, + email: null, + projectId: project1.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); + + const invite2 = models.ProjectMemberInvite.create({ + id: 2, + userId: testUtil.userIds.copilot, + email: 'test@topcoder.com', + projectId: project1.id, + role: 'copilot', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); + + return Promise.all([pm1, invite1, invite2]); }); - const invite2 = models.ProjectMemberInvite.create({ - id: 2, - userId: testUtil.userIds.copilot, - email: 'test@topcoder.com', - projectId: project1.id, - role: 'copilot', + const p2 = models.Project.create({ + type: 'visual_design', + billingAccountId: 1, + name: 'test2', + description: 'test project2', + status: 'draft', + details: {}, createdBy: 1, updatedBy: 1, - status: INVITE_STATUS.PENDING, - }); - - return Promise.all([pm1, invite1, invite2]); - }); + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project2 = p; - const p2 = models.Project.create({ - type: 'visual_design', - billingAccountId: 1, - name: 'test2', - description: 'test project2', - status: 'draft', - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project2 = p; + // create invite 3 + const invite3 = models.ProjectMemberInvite.create({ + id: 3, + userId: null, + email: 'test@topcoder.com', + projectId: project2.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); - // create invite 3 - const invite3 = models.ProjectMemberInvite.create({ - id: 3, - userId: null, - email: 'test@topcoder.com', - projectId: project2.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.PENDING, - }); + const invite4 = models.ProjectMemberInvite.create({ + id: 4, + userId: testUtil.userIds.member2, + email: null, + projectId: project2.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.ACCEPTED, + }); - const invite4 = models.ProjectMemberInvite.create({ - id: 4, - userId: testUtil.userIds.member2, - email: null, - projectId: project2.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.ACCEPTED, + return Promise.all([invite3, invite4]); }); - - return Promise.all([invite3, invite4]); - }); - return Promise.all([p1, p2]) + return Promise.all([p1, p2]) .then(() => done()); - }); + }); }); }); @@ -117,17 +117,17 @@ describe('GET Project Member Invite', () => { describe('GET /projects/{projectId}/invites/{inviteId}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get(`/v5/projects/${project2.id}/invites/1`) - .expect(403, done); + .get(`/v5/projects/${project2.id}/invites/1`) + .expect(403, done); }); it('should return 404 if requested project doesn\'t exist', (done) => { request(server) - .get('/v5/projects/14343323/invites/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); + .get('/v5/projects/14343323/invites/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, done); }); it('should return 404 if requested invitation doesn\'t exist', (done) => { @@ -168,48 +168,48 @@ describe('GET Project Member Invite', () => { it('should return the invite if user can view the project', (done) => { request(server) - .get(`/v5/projects/${project1.id}/invites/1`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - should.exist(resJson.projectId); - resJson.id.should.be.eql(1); - resJson.userId.should.be.eql(testUtil.userIds.member); - resJson.status.should.be.eql(INVITE_STATUS.PENDING); - done(); - } - }); + .get(`/v5/projects/${project1.id}/invites/1`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + should.exist(resJson.projectId); + resJson.id.should.be.eql(1); + resJson.userId.should.be.eql(testUtil.userIds.member); + resJson.status.should.be.eql(INVITE_STATUS.PENDING); + done(); + } + }); }); it('should return the invite using M2M token with "read:project-members" scope', (done) => { request(server) - .get(`/v5/projects/${project1.id}/invites/1`) - .set({ - Authorization: `Bearer ${testUtil.m2m['read:project-members']}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - should.exist(resJson.projectId); - resJson.id.should.be.eql(1); - resJson.userId.should.be.eql(testUtil.userIds.member); - resJson.status.should.be.eql(INVITE_STATUS.PENDING); - done(); - } - }); + .get(`/v5/projects/${project1.id}/invites/1`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-members']}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + should.exist(resJson.projectId); + resJson.id.should.be.eql(1); + resJson.userId.should.be.eql(testUtil.userIds.member); + resJson.status.should.be.eql(INVITE_STATUS.PENDING); + done(); + } + }); }); it('should return the invite if this invitation is for logged-in user', (done) => { diff --git a/src/routes/projectMemberInvites/list.spec.js b/src/routes/projectMemberInvites/list.spec.js index bcce0e8a..b5485587 100644 --- a/src/routes/projectMemberInvites/list.spec.js +++ b/src/routes/projectMemberInvites/list.spec.js @@ -17,97 +17,97 @@ describe('GET Project Member Invites', () => { // clear ES and db testUtil.clearES().then(() => { testUtil.clearDb() - .then(() => { - const p1 = models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - const pm1 = models.ProjectMember.create({ - userId: testUtil.userIds.admin, - projectId: project1.id, - role: 'copilot', - isPrimary: true, + .then(() => { + const p1 = models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, createdBy: 1, updatedBy: 1, - }); - // create invite - const invite1 = models.ProjectMemberInvite.create({ - id: 1, - userId: testUtil.userIds.member, - email: null, - projectId: project1.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.PENDING, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + const pm1 = models.ProjectMember.create({ + userId: testUtil.userIds.admin, + projectId: project1.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }); + // create invite + const invite1 = models.ProjectMemberInvite.create({ + id: 1, + userId: testUtil.userIds.member, + email: null, + projectId: project1.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); + + const invite2 = models.ProjectMemberInvite.create({ + id: 2, + userId: testUtil.userIds.copilot, + email: null, + projectId: project1.id, + role: 'copilot', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); + + return Promise.all([pm1, invite1, invite2]); }); - const invite2 = models.ProjectMemberInvite.create({ - id: 2, - userId: testUtil.userIds.copilot, - email: null, - projectId: project1.id, - role: 'copilot', + const p2 = models.Project.create({ + type: 'visual_design', + billingAccountId: 1, + name: 'test2', + description: 'test project2', + status: 'draft', + details: {}, createdBy: 1, updatedBy: 1, - status: INVITE_STATUS.PENDING, - }); + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project2 = p; - return Promise.all([pm1, invite1, invite2]); - }); - - const p2 = models.Project.create({ - type: 'visual_design', - billingAccountId: 1, - name: 'test2', - description: 'test project2', - status: 'draft', - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project2 = p; + // create invite 3 + const invite3 = models.ProjectMemberInvite.create({ + id: 3, + userId: null, + email: 'test@topcoder.com', + projectId: project2.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.PENDING, + }); - // create invite 3 - const invite3 = models.ProjectMemberInvite.create({ - id: 3, - userId: null, - email: 'test@topcoder.com', - projectId: project2.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.PENDING, - }); + const invite4 = models.ProjectMemberInvite.create({ + id: 4, + userId: testUtil.userIds.member2, + email: null, + projectId: project2.id, + role: 'customer', + createdBy: 1, + updatedBy: 1, + status: INVITE_STATUS.ACCEPTED, + }); - const invite4 = models.ProjectMemberInvite.create({ - id: 4, - userId: testUtil.userIds.member2, - email: null, - projectId: project2.id, - role: 'customer', - createdBy: 1, - updatedBy: 1, - status: INVITE_STATUS.ACCEPTED, + return Promise.all([invite3, invite4]); }); - - return Promise.all([invite3, invite4]); - }); - return Promise.all([p1, p2]) + return Promise.all([p1, p2]) .then(() => done()); - }); + }); }); }); @@ -118,99 +118,99 @@ describe('GET Project Member Invites', () => { describe('GET /projects/{projectId}/invites', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get(`/v5/projects/${project2.id}/invites`) - .expect(403, done); + .get(`/v5/projects/${project2.id}/invites`) + .expect(403, done); }); it('should return empty result if requested project doesn\'t exist', (done) => { request(server) - .get('/v5/projects/14343323/invites') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.be.an('array'); - resJson.length.should.be.eql(0); - done(); - } - }); + .get('/v5/projects/14343323/invites') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.be.an('array'); + resJson.length.should.be.eql(0); + done(); + } + }); }); it('should return all invitation if user can view the project', (done) => { request(server) - .get(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.be.an('array'); - resJson.length.should.be.eql(2); - // check invitations - _.filter(resJson, inv => inv.id === 1).length.should.be.eql(1); - _.filter(resJson, inv => inv.id === 2).length.should.be.eql(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.be.an('array'); + resJson.length.should.be.eql(2); + // check invitations + _.filter(resJson, inv => inv.id === 1).length.should.be.eql(1); + _.filter(resJson, inv => inv.id === 2).length.should.be.eql(1); + done(); + } + }); }); it('should get invites using M2M token with "read:project-members" scope', (done) => { request(server) - .get(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.m2m['read:project-members']}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.be.an('array'); - resJson.length.should.be.eql(2); - // check invitations - _.filter(resJson, inv => inv.id === 1).length.should.be.eql(1); - _.filter(resJson, inv => inv.id === 2).length.should.be.eql(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-members']}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.be.an('array'); + resJson.length.should.be.eql(2); + // check invitations + _.filter(resJson, inv => inv.id === 1).length.should.be.eql(1); + _.filter(resJson, inv => inv.id === 2).length.should.be.eql(1); + done(); + } + }); }); it('should return only pending/requested invitation if user can view the project', (done) => { request(server) - .get(`/v5/projects/${project2.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.be.an('array'); - resJson.length.should.be.eql(1); - // check invitations - _.filter(resJson, inv => inv.id === 3).length.should.be.eql(1); - done(); - } - }); + .get(`/v5/projects/${project2.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.be.an('array'); + resJson.length.should.be.eql(1); + // check invitations + _.filter(resJson, inv => inv.id === 3).length.should.be.eql(1); + done(); + } + }); }); it('should return only his/her invitation for logged-in user', (done) => { diff --git a/src/routes/projectMemberInvites/update.js b/src/routes/projectMemberInvites/update.js index 972ceb0e..10668474 100644 --- a/src/routes/projectMemberInvites/update.js +++ b/src/routes/projectMemberInvites/update.js @@ -15,12 +15,12 @@ const permissions = tcMiddleware.permissions; const updateMemberValidations = { body: Joi.object() - .keys({ - status: Joi.any() - .valid(_.values(INVITE_STATUS)) - .required(), - }) - .required(), + .keys({ + status: Joi.any() + .valid(_.values(INVITE_STATUS)) + .required(), + }) + .required(), }; module.exports = [ diff --git a/src/routes/projectMemberInvites/update.spec.js b/src/routes/projectMemberInvites/update.spec.js index 5c6c2657..db8ead48 100644 --- a/src/routes/projectMemberInvites/update.spec.js +++ b/src/routes/projectMemberInvites/update.spec.js @@ -408,65 +408,65 @@ describe('Project member invite update', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .patch(`/v5/projects/${project1.id}/invites/1`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send({ - status: INVITE_STATUS.ACCEPTED, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(5); - - // Events for accepted invite - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - userId: testUtil.userIds.member, - status: INVITE_STATUS.ACCEPTED, - email: null, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ - projectId: project1.id, - userId: testUtil.userIds.member, - status: INVITE_STATUS.ACCEPTED, - email: null, - isSSO: false, - })).should.be.true; - - // Events for created member (after invite acceptance) - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER, - projectId: project1.id, - userId: testUtil.userIds.member, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - userId: testUtil.userIds.member, - initiatorUserId: testUtil.userIds.member, - })).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - userId: testUtil.userIds.member, - initiatorUserId: testUtil.userIds.member, - })).should.be.true; - - done(); - }); - } - }); + .patch(`/v5/projects/${project1.id}/invites/1`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send({ + status: INVITE_STATUS.ACCEPTED, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(5); + + // Events for accepted invite + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + userId: testUtil.userIds.member, + status: INVITE_STATUS.ACCEPTED, + email: null, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ + projectId: project1.id, + userId: testUtil.userIds.member, + status: INVITE_STATUS.ACCEPTED, + email: null, + isSSO: false, + })).should.be.true; + + // Events for created member (after invite acceptance) + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER, + projectId: project1.id, + userId: testUtil.userIds.member, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + userId: testUtil.userIds.member, + initiatorUserId: testUtil.userIds.member, + })).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + userId: testUtil.userIds.member, + initiatorUserId: testUtil.userIds.member, + })).should.be.true; + + done(); + }); + } + }); }); }); }); diff --git a/src/routes/projectMembers/create.spec.js b/src/routes/projectMembers/create.spec.js index ea4c2f81..12234fc4 100644 --- a/src/routes/projectMembers/create.spec.js +++ b/src/routes/projectMembers/create.spec.js @@ -113,72 +113,72 @@ describe('Project Members create', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - handles: ['test_copilot1'], - role: 'copilot', - }) - .expect('Content-Type', /json/) - .expect(201) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body.success[0]; - should.exist(resJson); - resJson.role.should.equal('copilot'); - resJson.projectId.should.equal(project1.id); - resJson.userId.should.equal(40051332); - should.exist(resJson.id); - server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true; - request(server) - .patch(`/v5/projects/${project1.id}/invites/${resJson.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) - .send({ - status: 'accepted', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err2, res2) => { - if (err2) { - done(err2); - } else { - const resJson2 = res2.body; - should.exist(resJson2); - resJson2.role.should.equal('copilot'); - resJson2.projectId.should.equal(project1.id); - resJson2.userId.should.equal(40051332); - server.services.pubsub.publish.calledWith('project.member.invite.updated').should.be.true; - server.services.pubsub.publish.calledWith('project.member.added').should.be.true; + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + handles: ['test_copilot1'], + role: 'copilot', + }) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.success[0]; + should.exist(resJson); + resJson.role.should.equal('copilot'); + resJson.projectId.should.equal(project1.id); + resJson.userId.should.equal(40051332); + should.exist(resJson.id); + server.services.pubsub.publish.calledWith('project.member.invite.created').should.be.true; + request(server) + .patch(`/v5/projects/${project1.id}/invites/${resJson.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .send({ + status: 'accepted', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err2, res2) => { + if (err2) { + done(err2); + } else { + const resJson2 = res2.body; + should.exist(resJson2); + resJson2.role.should.equal('copilot'); + resJson2.projectId.should.equal(project1.id); + resJson2.userId.should.equal(40051332); + server.services.pubsub.publish.calledWith('project.member.invite.updated').should.be.true; + server.services.pubsub.publish.calledWith('project.member.added').should.be.true; - request(server) - .patch(`/v5/projects/${project1.id}/invites/${resJson.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) - .send({ - status: 'accepted', - }) - .expect('Content-Type', /json/) - .expect(404) - .end((err3, res3) => { - if (err3) { - done(err3); - } else { - const errorMessage = _.get(res3.body, 'message', ''); - sinon.assert.match(errorMessage, /.*invite not found for project id 1, inviteId/); - done(); - } - }); - } - }); - } - }); + request(server) + .patch(`/v5/projects/${project1.id}/invites/${resJson.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .send({ + status: 'accepted', + }) + .expect('Content-Type', /json/) + .expect(404) + .end((err3, res3) => { + if (err3) { + done(err3); + } else { + const errorMessage = _.get(res3.body, 'message', ''); + sinon.assert.match(errorMessage, /.*invite not found for project id 1, inviteId/); + done(); + } + }); + } + }); + } + }); }); it('should return 201 and register customer member', (done) => { @@ -398,38 +398,38 @@ describe('Project Members create', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .post(`/v5/projects/${project1.id}/members/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(3); + .post(`/v5/projects/${project1.id}/members/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(3); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER, - projectId: project1.id, - userId: 40051334, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER, + projectId: project1.id, + userId: 40051334, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED_MANAGER).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051334, - initiatorUserId: 40051334, - })).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED_MANAGER).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051334, + initiatorUserId: 40051334, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when copilot added', (done) => { @@ -468,85 +468,87 @@ describe('Project Members create', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .post(`/v5/projects/${project1.id}/invites`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member2}`, - }) - .send({ - handles: ['test_copilot1'], - role: 'copilot', - }) - .expect(201) - .end((err, inviteRes) => { - if (err) { - done(err); - } else { - const inviteResJson = inviteRes.body.success[0]; - should.exist(inviteResJson); - should.exist(inviteResJson.id); - request(server) - .patch(`/v5/projects/${project1.id}/invites/${inviteResJson.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) - .send({ - status: 'accepted', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err2) => { - if (err2) { - done(err2); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(7); + .post(`/v5/projects/${project1.id}/invites`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member2}`, + }) + .send({ + handles: ['test_copilot1'], + role: 'copilot', + }) + .expect(201) + .end((err, inviteRes) => { + if (err) { + done(err); + } else { + const inviteResJson = inviteRes.body.success[0]; + should.exist(inviteResJson); + should.exist(inviteResJson.id); + request(server) + .patch(`/v5/projects/${project1.id}/invites/${inviteResJson.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .send({ + status: 'accepted', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err2) => { + if (err2) { + done(err2); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(7); - /* + /* Copilot invitation requested */ - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - userId: 40051332, - email: null, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + userId: 40051332, + email: null, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REQUESTED).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_REQUESTED) + .should.be.true; - /* + /* Copilot invitation accepted */ - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER_INVITE, - projectId: project1.id, - userId: 40051332, - status: INVITE_STATUS.ACCEPTED, - email: null, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER_INVITE, + projectId: project1.id, + userId: 40051332, + status: INVITE_STATUS.ACCEPTED, + email: null, + })).should.be.true; - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER, - projectId: project1.id, - userId: 40051332, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_ADDED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER, + projectId: project1.id, + userId: 40051332, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED_COPILOT).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051332, - initiatorUserId: testUtil.userIds.connectAdmin, - })).should.be.true; - done(); + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_MEMBER_INVITE_UPDATED) + .should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.MEMBER_JOINED_COPILOT).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051332, + initiatorUserId: testUtil.userIds.connectAdmin, + })).should.be.true; + done(); + }); + } }); - } - }); - } - }); + } + }); }); }); }); diff --git a/src/routes/projectMembers/delete.js b/src/routes/projectMembers/delete.js index be7fd707..18d43702 100644 --- a/src/routes/projectMembers/delete.js +++ b/src/routes/projectMembers/delete.js @@ -21,81 +21,81 @@ module.exports = [ models.sequelize.transaction(() => // soft delete the record - models.ProjectMember.findOne({ - where: { id: memberRecordId, projectId }, - }) - .then((member) => { - if (!member) { - const err = new Error(`Project member not found for member id ${req.params.id}`); - err.status = 404; - return Promise.reject(err); - } + models.ProjectMember.findOne({ + where: { id: memberRecordId, projectId }, + }) + .then((member) => { + if (!member) { + const err = new Error(`Project member not found for member id ${req.params.id}`); + err.status = 404; + return Promise.reject(err); + } - if ( - member.userId !== req.authUser.userId && + if ( + member.userId !== req.authUser.userId && member.role !== PROJECT_MEMBER_ROLE.CUSTOMER && !util.hasPermissionByReq(PERMISSION.DELETE_PROJECT_MEMBER_NON_CUSTOMER, req) - ) { - const err = new Error('You don\'t have permissions to delete other members with non-customer role.'); - err.status = 403; - return Promise.reject(err); - } - return member.update({ deletedBy: req.authUser.userId }); - }) - .then(member => member.destroy({ logging: console.log })) // eslint-disable-line no-console - .then(member => member.save()) + ) { + const err = new Error('You don\'t have permissions to delete other members with non-customer role.'); + err.status = 403; + return Promise.reject(err); + } + return member.update({ deletedBy: req.authUser.userId }); + }) + .then(member => member.destroy({ logging: console.log })) // eslint-disable-line no-console + .then(member => member.save()) // if primary co-pilot is removed promote the next co-pilot to primary #43 - .then(member => new Promise((accept, reject) => { - if (member.role === PROJECT_MEMBER_ROLE.COPILOT && member.isPrimary) { + .then(member => new Promise((accept, reject) => { + if (member.role === PROJECT_MEMBER_ROLE.COPILOT && member.isPrimary) { // find the next copilot - models.ProjectMember.findAll({ - limit: 1, + models.ProjectMember.findAll({ + limit: 1, // return only non-deleted records - paranoid: true, - where: { - projectId, - role: PROJECT_MEMBER_ROLE.COPILOT, - }, - order: [['createdAt', 'ASC']], - }).then((members) => { - if (members && members.length > 0) { + paranoid: true, + where: { + projectId, + role: PROJECT_MEMBER_ROLE.COPILOT, + }, + order: [['createdAt', 'ASC']], + }).then((members) => { + if (members && members.length > 0) { // mark the copilot as primary - const nextMember = members[0]; - nextMember.set({ isPrimary: true }); - nextMember.save().then(() => { - accept(member); - }).catch((err) => { - reject(err); - }); - } else { + const nextMember = members[0]; + nextMember.set({ isPrimary: true }); + nextMember.save().then(() => { + accept(member); + }).catch((err) => { + reject(err); + }); + } else { // no copilot found nothing to do - accept(member); - } - }).catch((err) => { - reject(err); - }); - } else { + accept(member); + } + }).catch((err) => { + reject(err); + }); + } else { // nothing to do - accept(member); - } - }))).then((member) => { - // only return the response after transaction is committed - // fire event - const pmember = member.get({ plain: true }); - req.log.debug(pmember); - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_MEMBER_REMOVED, - pmember, - { correlationId: req.id }, - ); + accept(member); + } + }))).then((member) => { + // only return the response after transaction is committed + // fire event + const pmember = member.get({ plain: true }); + req.log.debug(pmember); + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_MEMBER_REMOVED, + pmember, + { correlationId: req.id }, + ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_MEMBER_REMOVED, - RESOURCES.PROJECT_MEMBER, - pmember); - res.status(204).json({}); - }).catch(err => next(err)); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_MEMBER_REMOVED, + RESOURCES.PROJECT_MEMBER, + pmember); + res.status(204).json({}); + }).catch(err => next(err)); }, ]; diff --git a/src/routes/projectMembers/delete.spec.js b/src/routes/projectMembers/delete.spec.js index 3834e39b..e9989df6 100644 --- a/src/routes/projectMembers/delete.spec.js +++ b/src/routes/projectMembers/delete.spec.js @@ -16,20 +16,20 @@ const should = chai.should(); const expectAfterDelete = (projectId, id, err, next) => { if (err) throw err; setTimeout(() => - models.ProjectMember.findOne({ - where: { - id, - projectId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - next(); - } - }), 500); + models.ProjectMember.findOne({ + where: { + id, + projectId, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + next(); + } + }), 500); }; describe('Project members delete', () => { let project1; diff --git a/src/routes/projectMembers/get.js b/src/routes/projectMembers/get.js index 0610a85a..f9ef759f 100644 --- a/src/routes/projectMembers/get.js +++ b/src/routes/projectMembers/get.js @@ -55,47 +55,47 @@ module.exports = [ }, }, }) - .then((data) => { - if (data.length === 0) { - req.log.debug('No project member found in ES'); - return models.ProjectMember.findOne({ - where: { - id: memberRecordId, - projectId }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((member) => { - if (!member) { - // check there is an existing member - const err = new Error(`member not found for project id ${projectId}, id ${memberRecordId}`); - err.status = 404; - throw err; - } - return member; - }); - } - req.log.debug('project member found in ES'); - return _.pick( - data[0].inner_hits.members.hits.hits[0]._source, // eslint-disable-line no-underscore-dangle - // Elasticsearch index might have additional fields added to members like - // 'handle', 'firstName', 'lastName', 'email' - // but we shouldn't return them, as they might be outdated - // method "getObjectsWithMemberDetails" would populate these fields again - // with up to date data from Member Service if necessary - PROJECT_MEMBER_ATTRIBUTES, - ); - }).then(member => ( - util.getObjectsWithMemberDetails([member], fields, req) - .then(([memberWithDetails]) => memberWithDetails) - .catch((err) => { - req.log.error('Cannot get user details for member.'); - req.log.debug('Error during getting user details for member.', err); - // continues without details anyway - return member; - }) - )) - .then(member => res.json(member)) - .catch(next); + .then((data) => { + if (data.length === 0) { + req.log.debug('No project member found in ES'); + return models.ProjectMember.findOne({ + where: { + id: memberRecordId, + projectId }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then((member) => { + if (!member) { + // check there is an existing member + const err = new Error(`member not found for project id ${projectId}, id ${memberRecordId}`); + err.status = 404; + throw err; + } + return member; + }); + } + req.log.debug('project member found in ES'); + return _.pick( + data[0].inner_hits.members.hits.hits[0]._source, // eslint-disable-line no-underscore-dangle + // Elasticsearch index might have additional fields added to members like + // 'handle', 'firstName', 'lastName', 'email' + // but we shouldn't return them, as they might be outdated + // method "getObjectsWithMemberDetails" would populate these fields again + // with up to date data from Member Service if necessary + PROJECT_MEMBER_ATTRIBUTES, + ); + }).then(member => ( + util.getObjectsWithMemberDetails([member], fields, req) + .then(([memberWithDetails]) => memberWithDetails) + .catch((err) => { + req.log.error('Cannot get user details for member.'); + req.log.debug('Error during getting user details for member.', err); + // continues without details anyway + return member; + }) + )) + .then(member => res.json(member)) + .catch(next); }, ]; diff --git a/src/routes/projectMembers/get.spec.js b/src/routes/projectMembers/get.spec.js index 5956d027..ee72c1a4 100644 --- a/src/routes/projectMembers/get.spec.js +++ b/src/routes/projectMembers/get.spec.js @@ -48,33 +48,33 @@ describe('GET project member', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - // create members - models.ProjectMember.create({ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }).then((_member) => { - memberId = _member.id; + .then((project) => { + projectId = project.id; + // create members models.ProjectMember.create({ - id: 2, - userId: memberUser.userId, + id: 1, + userId: copilotUser.userId, projectId, - role: 'customer', - isPrimary: true, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - }).then((m) => { - memberId2 = m.id; - done(); + }).then((_member) => { + memberId = _member.id; + models.ProjectMember.create({ + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then((m) => { + memberId2 = m.id; + done(); + }); }); }); - }); }); }); @@ -91,20 +91,20 @@ describe('GET project member', () => { it('should return 404 if requested project doesn\'t exist', (done) => { request(server) - .get('/v5/projects/9999999/members/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); + .get('/v5/projects/9999999/members/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, done); }); it('should return 404 if requested project member doesn\'t exist', (done) => { request(server) - .get(`/v5/projects/${projectId}/members/9999`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); + .get(`/v5/projects/${projectId}/members/9999`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, done); }); it('should return 200 for connect admin', (done) => { diff --git a/src/routes/projectMembers/list.js b/src/routes/projectMembers/list.js index f3e3b340..9db7ef0a 100644 --- a/src/routes/projectMembers/list.js +++ b/src/routes/projectMembers/list.js @@ -16,11 +16,11 @@ const permissions = tcMiddleware.permissions; const schema = { query: { role: Joi.any() - .valid(PROJECT_MEMBER_ROLE.MANAGER, - PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER, - PROJECT_MEMBER_ROLE.COPILOT, - PROJECT_MEMBER_ROLE.CUSTOMER, - PROJECT_MEMBER_ROLE.OBSERVER), + .valid(PROJECT_MEMBER_ROLE.MANAGER, + PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER, + PROJECT_MEMBER_ROLE.COPILOT, + PROJECT_MEMBER_ROLE.CUSTOMER, + PROJECT_MEMBER_ROLE.OBSERVER), fields: Joi.string().optional(), }, params: { @@ -70,47 +70,47 @@ module.exports = [ }, }, }) - .then((data) => { - if (data.length === 0) { - req.log.debug('No project members found in ES'); + .then((data) => { + if (data.length === 0) { + req.log.debug('No project members found in ES'); // Get all project members - const where = { - projectId, - }; - if (req.query.role) { - where.role = req.query.role; - } - return models.ProjectMember.findAll({ - where, - // Add order - order: [ + const where = { + projectId, + }; + if (req.query.role) { + where.role = req.query.role; + } + return models.ProjectMember.findAll({ + where, + // Add order + order: [ ['id', 'ASC'], - ], - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }); - } - req.log.debug('project members found in ES'); - return data[0].inner_hits.members.hits.hits.map(hit => _.pick( - hit._source, // eslint-disable-line no-underscore-dangle - // Elasticsearch index might have additional fields added to members like - // 'handle', 'firstName', 'lastName', 'email' - // but we shouldn't return them, as they might be outdated - // method "getObjectsWithMemberDetails" would populate these fields again - // with up to date data from Member Service if necessary - PROJECT_MEMBER_ATTRIBUTES, - )); - }) - .then(members => ( - util.getObjectsWithMemberDetails(members, fields, req) - .catch((err) => { - req.log.error('Cannot get user details for member.'); - req.log.debug('Error during getting user details for member.', err); - // continues without details anyway - return members; - }) - )) - .then(members => res.json(members)) - .catch(next); + ], + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }); + } + req.log.debug('project members found in ES'); + return data[0].inner_hits.members.hits.hits.map(hit => _.pick( + hit._source, // eslint-disable-line no-underscore-dangle + // Elasticsearch index might have additional fields added to members like + // 'handle', 'firstName', 'lastName', 'email' + // but we shouldn't return them, as they might be outdated + // method "getObjectsWithMemberDetails" would populate these fields again + // with up to date data from Member Service if necessary + PROJECT_MEMBER_ATTRIBUTES, + )); + }) + .then(members => ( + util.getObjectsWithMemberDetails(members, fields, req) + .catch((err) => { + req.log.error('Cannot get user details for member.'); + req.log.debug('Error during getting user details for member.', err); + // continues without details anyway + return members; + }) + )) + .then(members => res.json(members)) + .catch(next); }, ]; diff --git a/src/routes/projectMembers/list.spec.js b/src/routes/projectMembers/list.spec.js index d9ef80d5..4db2595b 100644 --- a/src/routes/projectMembers/list.spec.js +++ b/src/routes/projectMembers/list.spec.js @@ -45,29 +45,29 @@ describe('LIST project members', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - id = project.id; - // create members - models.ProjectMember.create({ - id: 1, - userId: copilotUser.userId, - projectId: id, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }).then(() => { + .then((project) => { + id = project.id; + // create members models.ProjectMember.create({ - id: 2, - userId: memberUser.userId, + id: 1, + userId: copilotUser.userId, projectId: id, - role: 'customer', - isPrimary: true, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - }).then(() => done()); + }).then(() => { + models.ProjectMember.create({ + id: 2, + userId: memberUser.userId, + projectId: id, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then(() => done()); + }); }); - }); }); }); diff --git a/src/routes/projectMembers/update.js b/src/routes/projectMembers/update.js index fffe69f3..ddf318b1 100644 --- a/src/routes/projectMembers/update.js +++ b/src/routes/projectMembers/update.js @@ -37,7 +37,7 @@ module.exports = [ // handles request validations validate(updateProjectMemberValdiations), permissions('projectMember.edit'), - /** + /* * Update a projectMember if the user has access */ (req, res, next) => { @@ -53,104 +53,104 @@ module.exports = [ models.sequelize.transaction(() => models.ProjectMember.findOne({ where: { id: memberRecordId, projectId }, }) - .then((_member) => { - if (!_member) { - // handle 404 - const err = new Error(`project member not found for project id ${projectId} ` + + .then((_member) => { + if (!_member) { + // handle 404 + const err = new Error(`project member not found for project id ${projectId} ` + `and member id ${memberRecordId}`); - err.status = 404; - return Promise.reject(err); - } + err.status = 404; + return Promise.reject(err); + } - projectMember = _member; - previousValue = _.clone(projectMember.get({ plain: true })); - _.assign(projectMember, updatedProps); + projectMember = _member; + previousValue = _.clone(projectMember.get({ plain: true })); + _.assign(projectMember, updatedProps); - if ( - previousValue.userId !== req.authUser.userId && + if ( + previousValue.userId !== req.authUser.userId && previousValue.role !== PROJECT_MEMBER_ROLE.CUSTOMER && !util.hasPermissionByReq(PERMISSION.UPDATE_PROJECT_MEMBER_NON_CUSTOMER, req) - ) { - const err = new Error('You don\'t have permission to update a non-customer member.'); - err.status = 403; - return Promise.reject(err); - } + ) { + const err = new Error('You don\'t have permission to update a non-customer member.'); + err.status = 403; + return Promise.reject(err); + } - // no updates if no change - if (updatedProps.role === previousValue.role && + // no updates if no change + if (updatedProps.role === previousValue.role && (_.isUndefined(updatedProps.isPrimary) || updatedProps.isPrimary === previousValue.isPrimary)) { - return Promise.resolve(); - } + return Promise.resolve(); + } - return util.getUserRoles(projectMember.userId, req.log, req.id).then((roles) => { - if ( - previousValue.role !== updatedProps.role && + return util.getUserRoles(projectMember.userId, req.log, req.id).then((roles) => { + if ( + previousValue.role !== updatedProps.role && !util.matchPermissionRule( { topcoderRoles: PROJECT_TO_TOPCODER_ROLES_MATRIX[updatedProps.role] }, { roles }, ) - ) { - const err = new Error( - `User doesn't have required Topcoder roles to have project role "${updatedProps.role}".`, - ); - err.status = 401; - throw err; - } + ) { + const err = new Error( + `User doesn't have required Topcoder roles to have project role "${updatedProps.role}".`, + ); + err.status = 401; + throw err; + } - projectMember.updatedBy = req.authUser.userId; - const operations = []; - operations.push(projectMember.save()); + projectMember.updatedBy = req.authUser.userId; + const operations = []; + operations.push(projectMember.save()); - if (updatedProps.isPrimary) { - // if set as primary, other users with same role should no longer be primary - operations.push(models.ProjectMember.update({ isPrimary: false, - updatedBy: req.authUser.userId }, - { - where: { - projectId, - isPrimary: true, - role: updatedProps.role, - id: { - $ne: projectMember.id, - }, - }, - })); - } - return Promise.all(operations); - }); - }) - .then(() => projectMember.reload(projectMember.id)) - .then(() => { - projectMember = projectMember.get({ plain: true }); - projectMember = _.omit(projectMember, ['deletedAt']); - }) - .then(() => ( - util.getObjectsWithMemberDetails([projectMember], fields, req) - .then(([memberWithDetails]) => memberWithDetails) - .catch((err) => { - req.log.error('Cannot get user details for member.'); - req.log.debug('Error during getting user details for member.', err); - // continues without details anyway - return projectMember; - }) - )) - .then((memberWithDetails) => { - // emit original and updated project information - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED, - { original: previousValue, updated: projectMember }, - { correlationId: req.id }, - ); - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED, - RESOURCES.PROJECT_MEMBER, - projectMember, - previousValue); - req.log.debug('updated project member', projectMember); - res.json(memberWithDetails || projectMember); - }) - .catch(err => next(err))); + if (updatedProps.isPrimary) { + // if set as primary, other users with same role should no longer be primary + operations.push(models.ProjectMember.update({ isPrimary: false, + updatedBy: req.authUser.userId }, + { + where: { + projectId, + isPrimary: true, + role: updatedProps.role, + id: { + $ne: projectMember.id, + }, + }, + })); + } + return Promise.all(operations); + }); + }) + .then(() => projectMember.reload(projectMember.id)) + .then(() => { + projectMember = projectMember.get({ plain: true }); + projectMember = _.omit(projectMember, ['deletedAt']); + }) + .then(() => ( + util.getObjectsWithMemberDetails([projectMember], fields, req) + .then(([memberWithDetails]) => memberWithDetails) + .catch((err) => { + req.log.error('Cannot get user details for member.'); + req.log.debug('Error during getting user details for member.', err); + // continues without details anyway + return projectMember; + }) + )) + .then((memberWithDetails) => { + // emit original and updated project information + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED, + { original: previousValue, updated: projectMember }, + { correlationId: req.id }, + ); + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED, + RESOURCES.PROJECT_MEMBER, + projectMember, + previousValue); + req.log.debug('updated project member', projectMember); + res.json(memberWithDetails || projectMember); + }) + .catch(err => next(err))); }, ]; diff --git a/src/routes/projectMembers/update.spec.js b/src/routes/projectMembers/update.spec.js index 8874e3b0..44ce364d 100644 --- a/src/routes/projectMembers/update.spec.js +++ b/src/routes/projectMembers/update.spec.js @@ -506,42 +506,42 @@ describe('Project members update', () => { }); sandbox.stub(util, 'getHttpClient', () => mockHttpClient); request(server) - .patch(`/v5/projects/${project1.id}/members/${member2.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) - .send({ - role: 'customer', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(2); + .patch(`/v5/projects/${project1.id}/members/${member2.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .send({ + role: 'customer', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_UPDATED, sinon.match({ - resource: RESOURCES.PROJECT_MEMBER, - id: member2.id, - role: 'customer', - userId: 40051332, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_MEMBER_UPDATED, sinon.match({ + resource: RESOURCES.PROJECT_MEMBER, + id: member2.id, + role: 'customer', + userId: 40051332, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051332, - initiatorUserId: testUtil.userIds.manager, - })).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051332, + initiatorUserId: testUtil.userIds.manager, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); }); diff --git a/src/routes/projectReports/getEmbedReport.js b/src/routes/projectReports/getEmbedReport.js index e43dd7ce..3d265029 100644 --- a/src/routes/projectReports/getEmbedReport.js +++ b/src/routes/projectReports/getEmbedReport.js @@ -53,17 +53,17 @@ module.exports = [ // if no project template found, try to find product template (for old project v2) } else { const productTemplate = _.get(project, 'details.products[0]') - ? await models.ProductTemplate.findOne( - { - where: { - productKey: _.get(project, 'details.products[0]'), + ? await models.ProductTemplate.findOne( + { + where: { + productKey: _.get(project, 'details.products[0]'), + }, }, - }, - { - attributes: ['category'], - raw: true, - }, - ) : null; + { + attributes: ['category'], + raw: true, + }, + ) : null; category = _.get(productTemplate, 'category', ''); } diff --git a/src/routes/projectReports/getReport.spec.js b/src/routes/projectReports/getReport.spec.js index 9bdf5d3c..0bb426fd 100644 --- a/src/routes/projectReports/getReport.spec.js +++ b/src/routes/projectReports/getReport.spec.js @@ -56,7 +56,7 @@ describe('GET report', () => { }).then(() => { done(); }), - ), + ), ); }); }); diff --git a/src/routes/projectSettings/create.js b/src/routes/projectSettings/create.js index 6623a723..e42de99d 100644 --- a/src/routes/projectSettings/create.js +++ b/src/routes/projectSettings/create.js @@ -79,7 +79,7 @@ module.exports = [ return Promise.resolve(); }), - ) // transaction end + ) // transaction end .then(() => { req.log.debug('new project setting created (id# %d, key: %s)', setting.id, setting.key); diff --git a/src/routes/projectSettings/create.spec.js b/src/routes/projectSettings/create.spec.js index 152ee7fa..f75812e9 100644 --- a/src/routes/projectSettings/create.spec.js +++ b/src/routes/projectSettings/create.spec.js @@ -117,15 +117,15 @@ describe('CREATE Project Setting', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - - models.ProjectEstimation.create(_.assign(estimation, { projectId })) - .then((e) => { - estimationId = e.id; - done(); + .then((project) => { + projectId = project.id; + + models.ProjectEstimation.create(_.assign(estimation, { projectId })) + .then((e) => { + estimationId = e.id; + done(); + }); }); - }); }); }); @@ -293,34 +293,34 @@ describe('CREATE Project Setting', () => { it('should return 201 for manager, calculating project estimation items', (done) => { request(server) - .post(`/v5/projects/${projectId}/settings`) - .set({ - Authorization: `Bearer ${testUtil.jwts.manager}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) - .end((err, res) => { - if (err) done(err); + .post(`/v5/projects/${projectId}/settings`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) done(err); - const resJson = res.body; - resJson.key.should.be.eql(body.key); - resJson.value.should.be.eql(body.value); - resJson.valueType.should.be.eql(body.valueType); - resJson.projectId.should.be.eql(projectId); - resJson.createdBy.should.be.eql(40051334); - should.exist(resJson.createdAt); - resJson.updatedBy.should.be.eql(40051334); - should.exist(resJson.updatedAt); - should.not.exist(resJson.deletedBy); - should.not.exist(resJson.deletedAt); - expectAfterCreate(resJson.id, projectId, _.assign(estimation, { - id: estimationId, - value: body.value, - valueType: body.valueType, - key: body.key, - }), 1, 0, err, done); - }); + const resJson = res.body; + resJson.key.should.be.eql(body.key); + resJson.value.should.be.eql(body.value); + resJson.valueType.should.be.eql(body.valueType); + resJson.projectId.should.be.eql(projectId); + resJson.createdBy.should.be.eql(40051334); + should.exist(resJson.createdAt); + resJson.updatedBy.should.be.eql(40051334); + should.exist(resJson.updatedAt); + should.not.exist(resJson.deletedBy); + should.not.exist(resJson.deletedAt); + expectAfterCreate(resJson.id, projectId, _.assign(estimation, { + id: estimationId, + value: body.value, + valueType: body.valueType, + key: body.key, + }), 1, 0, err, done); + }); }); it('should return 201 for admin', (done) => { diff --git a/src/routes/projectSettings/delete.js b/src/routes/projectSettings/delete.js index aa85f493..b625fcef 100644 --- a/src/routes/projectSettings/delete.js +++ b/src/routes/projectSettings/delete.js @@ -32,32 +32,32 @@ module.exports = [ projectId, }, }) - .then((entity) => { + .then((entity) => { // Not found - if (!entity) { - const apiErr = new Error(`Project setting not found for id ${id} and project id ${projectId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!entity) { + const apiErr = new Error(`Project setting not found for id ${id} and project id ${projectId}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - deletedEntity = entity; - // Update the deletedBy, then delete - return entity.update({ deletedBy: req.authUser.userId }); - }) - .then(entity => entity.destroy()) - .then(() => { + deletedEntity = entity; + // Update the deletedBy, then delete + return entity.update({ deletedBy: req.authUser.userId }); + }) + .then(entity => entity.destroy()) + .then(() => { // Calculate for valid estimation type - if (util.isProjectSettingForEstimation(deletedEntity.key)) { - req.log.debug(`Recalculate price breakdown for project id ${projectId}`); - return util.calculateProjectEstimationItems(req, projectId); - } + if (util.isProjectSettingForEstimation(deletedEntity.key)) { + req.log.debug(`Recalculate price breakdown for project id ${projectId}`); + return util.calculateProjectEstimationItems(req, projectId); + } - return Promise.resolve(); - }), + return Promise.resolve(); + }), ) // transaction end - .then(() => { - res.status(204).end(); - }) - .catch(next); + .then(() => { + res.status(204).end(); + }) + .catch(next); }, ]; diff --git a/src/routes/projectSettings/delete.spec.js b/src/routes/projectSettings/delete.spec.js index 78d1e514..3bce216f 100644 --- a/src/routes/projectSettings/delete.spec.js +++ b/src/routes/projectSettings/delete.spec.js @@ -22,39 +22,39 @@ const expectAfterDelete = (id, projectId, len, deletedLen, err, next) => { }, paranoid: false, }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - should.exist(res.deletedBy); - should.exist(res.deletedAt); + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + should.exist(res.deletedBy); + should.exist(res.deletedAt); - // find deleted ProjectEstimationItems for project - models.ProjectEstimationItem.findAllByProject(models, projectId, { - where: { - deletedAt: { $ne: null }, - }, - includeAllProjectEstimatinoItemsForInternalUsage: true, - paranoid: false, - }).then((items) => { + // find deleted ProjectEstimationItems for project + models.ProjectEstimationItem.findAllByProject(models, projectId, { + where: { + deletedAt: { $ne: null }, + }, + includeAllProjectEstimatinoItemsForInternalUsage: true, + paranoid: false, + }).then((items) => { // deleted project estimation items - items.should.have.lengthOf(deletedLen, 'Number of deleted ProjectEstimationItems doesn\'t match'); - _.each(items, (item) => { - should.exist(item.deletedBy); - should.exist(item.deletedAt); - }); + items.should.have.lengthOf(deletedLen, 'Number of deleted ProjectEstimationItems doesn\'t match'); + _.each(items, (item) => { + should.exist(item.deletedBy); + should.exist(item.deletedAt); + }); - // find (non-deleted) ProjectEstimationItems for project - return models.ProjectEstimationItem.findAllByProject(models, projectId, { - includeAllProjectEstimatinoItemsForInternalUsage: true, - }); - }).then((items) => { + // find (non-deleted) ProjectEstimationItems for project + return models.ProjectEstimationItem.findAllByProject(models, projectId, { + includeAllProjectEstimatinoItemsForInternalUsage: true, + }); + }).then((items) => { // all non-deleted project estimation item count - items.should.have.lengthOf(len, 'Number of created ProjectEstimationItems doesn\'t match'); - next(); - }).catch(next); - } - }); + items.should.have.lengthOf(len, 'Number of created ProjectEstimationItems doesn\'t match'); + next(); + }).catch(next); + } + }); }; describe('DELETE Project Setting', () => { @@ -93,54 +93,54 @@ describe('DELETE Project Setting', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; + .then((project) => { + projectId = project.id; - models.ProjectSetting.bulkCreate([{ - projectId, - key: 'markup_topcoder_service', - value: '5599.96', - valueType: 'double', - readPermission: { - projectRoles: ['customer'], - topcoderRoles: ['administrator'], - }, - writePermission: { - allowRule: { - projectRoles: ['customer', 'copilot'], + models.ProjectSetting.bulkCreate([{ + projectId, + key: 'markup_topcoder_service', + value: '5599.96', + valueType: 'double', + readPermission: { + projectRoles: ['customer'], topcoderRoles: ['administrator'], }, - denyRule: { - projectRoles: ['copilot'], + writePermission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['administrator'], + }, + denyRule: { + projectRoles: ['copilot'], + }, }, - }, - createdBy: 1, - updatedBy: 1, - }, { - projectId, - key: 'markup_no_estimation', - value: '40', - valueType: 'percentage', - readPermission: { - topcoderRoles: ['administrator'], - }, - writePermission: { - allowRule: { topcoderRoles: ['administrator'] }, - denyRule: { projectRoles: ['copilot'] }, - }, - createdBy: 1, - updatedBy: 1, - }], { returning: true }) - .then((settings) => { - id = settings[0].id; - id2 = settings[1].id; - models.ProjectEstimation.create(_.assign(estimation, { projectId })) - .then((e) => { - estimationId = e.id; - done(); - }); + createdBy: 1, + updatedBy: 1, + }, { + projectId, + key: 'markup_no_estimation', + value: '40', + valueType: 'percentage', + readPermission: { + topcoderRoles: ['administrator'], + }, + writePermission: { + allowRule: { topcoderRoles: ['administrator'] }, + denyRule: { projectRoles: ['copilot'] }, + }, + createdBy: 1, + updatedBy: 1, + }], { returning: true }) + .then((settings) => { + id = settings[0].id; + id2 = settings[1].id; + models.ProjectEstimation.create(_.assign(estimation, { projectId })) + .then((e) => { + estimationId = e.id; + done(); + }); + }); }); - }); }); }); @@ -213,69 +213,69 @@ describe('DELETE Project Setting', () => { createdBy: 1, updatedBy: 1, }) - .then(() => { + .then(() => { + request(server) + .delete(`/v5/projects/${projectId}/settings/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end(err => expectAfterDelete(id, projectId, 0, 1, err, done)); + }).catch(done); + }); + + it('should return 204, for admin, if project setting with non-estimation type was successfully removed', + (done) => { request(server) - .delete(`/v5/projects/${projectId}/settings/${id}`) + .delete(`/v5/projects/${projectId}/settings/${id2}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect(204) - .end(err => expectAfterDelete(id, projectId, 0, 1, err, done)); - }).catch(done); - }); - - it('should return 204, for admin, if project setting with non-estimation type was successfully removed', - (done) => { - request(server) - .delete(`/v5/projects/${projectId}/settings/${id2}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(204) - .end(err => expectAfterDelete(id2, projectId, 0, 0, err, done)); - }); + .end(err => expectAfterDelete(id2, projectId, 0, 0, err, done)); + }); it('should return 204, for admin, another project setting exists if the project setting was successfully removed', - (done) => { - models.ProjectSetting.create({ - projectId, - key: 'markup_fee', - value: '25', - valueType: 'percentage', - readPermission: { - projectRoles: ['customer'], - topcoderRoles: ['administrator'], - }, - writePermission: { - allowRule: { - projectRoles: ['customer', 'copilot'], + (done) => { + models.ProjectSetting.create({ + projectId, + key: 'markup_fee', + value: '25', + valueType: 'percentage', + readPermission: { + projectRoles: ['customer'], topcoderRoles: ['administrator'], }, - denyRule: { - projectRoles: ['copilot'], + writePermission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['administrator'], + }, + denyRule: { + projectRoles: ['copilot'], + }, }, - }, - createdBy: 1, - updatedBy: 1, - }).then((anotherSetting) => { - models.ProjectEstimationItem.create({ - projectEstimationId: estimationId, - price: 1200, - type: 'fee', - markupUsedReference: 'projectSetting', - markupUsedReferenceId: anotherSetting.id, createdBy: 1, updatedBy: 1, - }).then(() => { - request(server) - .delete(`/v5/projects/${projectId}/settings/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(204) - .end(err => expectAfterDelete(id, projectId, 1, 1, err, done)); - }); - }).catch(done); - }); + }).then((anotherSetting) => { + models.ProjectEstimationItem.create({ + projectEstimationId: estimationId, + price: 1200, + type: 'fee', + markupUsedReference: 'projectSetting', + markupUsedReferenceId: anotherSetting.id, + createdBy: 1, + updatedBy: 1, + }).then(() => { + request(server) + .delete(`/v5/projects/${projectId}/settings/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end(err => expectAfterDelete(id, projectId, 1, 1, err, done)); + }); + }).catch(done); + }); }); }); diff --git a/src/routes/projectSettings/list.spec.js b/src/routes/projectSettings/list.spec.js index f5e0b597..6248aaae 100644 --- a/src/routes/projectSettings/list.spec.js +++ b/src/routes/projectSettings/list.spec.js @@ -79,30 +79,30 @@ describe('LIST Project Settings', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]) - .then(() => { - models.ProjectSetting.bulkCreate(_.map(settings, s => _.assign(s, { projectId }))).then(() => done()); + .then((project) => { + projectId = project.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]) + .then(() => { + models.ProjectSetting.bulkCreate(_.map(settings, s => _.assign(s, { projectId }))).then(() => done()); + }); }); - }); }); }); diff --git a/src/routes/projectSettings/update.js b/src/routes/projectSettings/update.js index 9f22b1d9..8029776a 100644 --- a/src/routes/projectSettings/update.js +++ b/src/routes/projectSettings/update.js @@ -46,29 +46,29 @@ module.exports = [ projectId, }, }) - .then((existing) => { + .then((existing) => { // Not found - if (!existing) { - const apiErr = new Error(`Project setting not found for id ${id} and project id ${projectId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!existing) { + const apiErr = new Error(`Project setting not found for id ${id} and project id ${projectId}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - oldKey = existing.key; - return existing.update(entityToUpdate); - }) - .then((updated) => { - updatedSetting = updated; - if (util.isProjectSettingForEstimation(updatedSetting.key) || util.isProjectSettingForEstimation(oldKey)) { - req.log.debug(`Recalculate price breakdown for project id ${projectId}`); - return util.calculateProjectEstimationItems(req, projectId); - } - return Promise.resolve(); - }), + oldKey = existing.key; + return existing.update(entityToUpdate); + }) + .then((updated) => { + updatedSetting = updated; + if (util.isProjectSettingForEstimation(updatedSetting.key) || util.isProjectSettingForEstimation(oldKey)) { + req.log.debug(`Recalculate price breakdown for project id ${projectId}`); + return util.calculateProjectEstimationItems(req, projectId); + } + return Promise.resolve(); + }), ) // transaction end - .then(() => { - res.json(updatedSetting); - }) - .catch(next); + .then(() => { + res.json(updatedSetting); + }) + .catch(next); }, ]; diff --git a/src/routes/projectSettings/update.spec.js b/src/routes/projectSettings/update.spec.js index 916757f2..1fbd4779 100644 --- a/src/routes/projectSettings/update.spec.js +++ b/src/routes/projectSettings/update.spec.js @@ -22,51 +22,51 @@ const expectAfterUpdate = (id, projectId, estimation, len, deletedLen, err, next projectId, }, }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { // find deleted ProjectEstimationItems for project - models.ProjectEstimationItem.findAllByProject(models, projectId, { - where: { - deletedAt: { $ne: null }, - }, - includeAllProjectEstimatinoItemsForInternalUsage: true, - paranoid: false, - }).then((items) => { + models.ProjectEstimationItem.findAllByProject(models, projectId, { + where: { + deletedAt: { $ne: null }, + }, + includeAllProjectEstimatinoItemsForInternalUsage: true, + paranoid: false, + }).then((items) => { // deleted project estimation items - items.should.have.lengthOf(deletedLen, 'Number of deleted ProjectEstimationItems doesn\'t match'); + items.should.have.lengthOf(deletedLen, 'Number of deleted ProjectEstimationItems doesn\'t match'); - _.each(items, (item) => { - should.exist(item.deletedBy); - should.exist(item.deletedAt); - }); + _.each(items, (item) => { + should.exist(item.deletedBy); + should.exist(item.deletedAt); + }); - // find (non-deleted) ProjectEstimationItems for project - return models.ProjectEstimationItem.findAllByProject(models, projectId, { - includeAllProjectEstimatinoItemsForInternalUsage: true, - }); - }).then((entities) => { - entities.should.have.lengthOf(len, 'Number of created ProjectEstimationItems doesn\'t match'); - if (len) { - entities[0].projectEstimationId.should.be.eql(estimation.id); - if (estimation.valueType === VALUE_TYPE.PERCENTAGE) { - entities[0].price.should.be.eql((estimation.price * estimation.value) / 100); - } else { - entities[0].price.should.be.eql(Number(estimation.value)); + // find (non-deleted) ProjectEstimationItems for project + return models.ProjectEstimationItem.findAllByProject(models, projectId, { + includeAllProjectEstimatinoItemsForInternalUsage: true, + }); + }).then((entities) => { + entities.should.have.lengthOf(len, 'Number of created ProjectEstimationItems doesn\'t match'); + if (len) { + entities[0].projectEstimationId.should.be.eql(estimation.id); + if (estimation.valueType === VALUE_TYPE.PERCENTAGE) { + entities[0].price.should.be.eql((estimation.price * estimation.value) / 100); + } else { + entities[0].price.should.be.eql(Number(estimation.value)); + } + entities[0].type.should.be.eql(estimation.key.split('markup_')[1]); + entities[0].markupUsedReference.should.be.eql('projectSetting'); + entities[0].markupUsedReferenceId.should.be.eql(id); + should.exist(entities[0].updatedAt); + should.not.exist(entities[0].deletedBy); + should.not.exist(entities[0].deletedAt); } - entities[0].type.should.be.eql(estimation.key.split('markup_')[1]); - entities[0].markupUsedReference.should.be.eql('projectSetting'); - entities[0].markupUsedReferenceId.should.be.eql(id); - should.exist(entities[0].updatedAt); - should.not.exist(entities[0].deletedBy); - should.not.exist(entities[0].deletedAt); - } - - next(); - }); - } - }); + + next(); + }); + } + }); }; describe('UPDATE Project Setting', () => { @@ -146,41 +146,41 @@ describe('UPDATE Project Setting', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]) - .then(() => { - models.ProjectSetting.create(_.assign({}, body, bodyNonMutable, { + .then((project) => { + projectId = project.id; + + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, projectId, - })) - .then((s) => { - id = s.id; - - models.ProjectEstimation.create(_.assign(estimation, { projectId })) - .then((e) => { - estimationId = e.id; - done(); + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]) + .then(() => { + models.ProjectSetting.create(_.assign({}, body, bodyNonMutable, { + projectId, + })) + .then((s) => { + id = s.id; + + models.ProjectEstimation.create(_.assign(estimation, { projectId })) + .then((e) => { + estimationId = e.id; + done(); + }); + }).catch(done); }); - }).catch(done); }); - }); }); }); @@ -272,55 +272,55 @@ describe('UPDATE Project Setting', () => { }); it('should return 200, for member with permission (team member), value updated but no project estimation present', - (done) => { - const notPresent = _.cloneDeep(body); - notPresent.value = '4500'; - - models.ProjectEstimation.destroy({ - where: { - id: estimationId, - }, - }).then(() => { - models.ProjectEstimationItem.destroy({ + (done) => { + const notPresent = _.cloneDeep(body); + notPresent.value = '4500'; + + models.ProjectEstimation.destroy({ where: { - markupUsedReference: 'projectSetting', - markupUsedReferenceId: id, + id: estimationId, }, }).then(() => { - request(server) - .patch(`/v5/projects/${projectId}/settings/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send({ - value: notPresent.value, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) done(err); - - const resJson = res.body; - resJson.id.should.be.eql(id); - resJson.key.should.be.eql(bodyNonMutable.key); - resJson.value.should.be.eql(notPresent.value); - resJson.valueType.should.be.eql(notPresent.valueType); - resJson.projectId.should.be.eql(projectId); - resJson.createdBy.should.be.eql(bodyNonMutable.createdBy); - resJson.updatedBy.should.be.eql(40051331); - should.exist(resJson.updatedAt); - should.not.exist(resJson.deletedBy); - should.not.exist(resJson.deletedAt); - expectAfterUpdate(id, projectId, _.assign(estimation, { - id: estimationId, + models.ProjectEstimationItem.destroy({ + where: { + markupUsedReference: 'projectSetting', + markupUsedReferenceId: id, + }, + }).then(() => { + request(server) + .patch(`/v5/projects/${projectId}/settings/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send({ value: notPresent.value, - valueType: notPresent.valueType, - key: bodyNonMutable.key, - }), 0, 0, err, done); - }); - }); - }).catch(done); - }); + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) done(err); + + const resJson = res.body; + resJson.id.should.be.eql(id); + resJson.key.should.be.eql(bodyNonMutable.key); + resJson.value.should.be.eql(notPresent.value); + resJson.valueType.should.be.eql(notPresent.valueType); + resJson.projectId.should.be.eql(projectId); + resJson.createdBy.should.be.eql(bodyNonMutable.createdBy); + resJson.updatedBy.should.be.eql(40051331); + should.exist(resJson.updatedAt); + should.not.exist(resJson.deletedBy); + should.not.exist(resJson.deletedAt); + expectAfterUpdate(id, projectId, _.assign(estimation, { + id: estimationId, + value: notPresent.value, + valueType: notPresent.valueType, + key: bodyNonMutable.key, + }), 0, 0, err, done); + }); + }); + }).catch(done); + }); it('should return 200 for admin when value updated, calculating project estimation items', (done) => { body.value = '4500'; diff --git a/src/routes/projectTemplates/create.js b/src/routes/projectTemplates/create.js index 473139f7..90ebf8b3 100644 --- a/src/routes/projectTemplates/create.js +++ b/src/routes/projectTemplates/create.js @@ -47,10 +47,10 @@ const schema = { updatedBy: Joi.any().strip(), deletedBy: Joi.any().strip(), }) - .xor('form', 'scope') - .xor('phases', 'planConfig') - .nand('priceConfig', 'scope') - .required(), + .xor('form', 'scope') + .xor('phases', 'planConfig') + .nand('priceConfig', 'scope') + .required(), }; module.exports = [ diff --git a/src/routes/projectTemplates/delete.js b/src/routes/projectTemplates/delete.js index aaf8055d..2530d429 100644 --- a/src/routes/projectTemplates/delete.js +++ b/src/routes/projectTemplates/delete.js @@ -33,17 +33,17 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then((entity) => { - // emit event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PROJECT_TEMPLATE, - _.pick(entity.toJSON(), 'id'), - ); + .then((entity) => { + // emit event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PROJECT_TEMPLATE, + _.pick(entity.toJSON(), 'id'), + ); - res.status(204).end(); - }) - .catch(next); + res.status(204).end(); + }) + .catch(next); }, ]; diff --git a/src/routes/projectTemplates/delete.spec.js b/src/routes/projectTemplates/delete.spec.js index 580f8504..d283efcd 100644 --- a/src/routes/projectTemplates/delete.spec.js +++ b/src/routes/projectTemplates/delete.spec.js @@ -11,27 +11,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (id, err, next) => { if (err) throw err; setTimeout(() => - models.ProjectTemplate.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.ProjectTemplate.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/metadata/projectTemplates/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/metadata/projectTemplates/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE project template', () => { diff --git a/src/routes/projectTemplates/get.js b/src/routes/projectTemplates/get.js index db464bda..afeb733e 100644 --- a/src/routes/projectTemplates/get.js +++ b/src/routes/projectTemplates/get.js @@ -30,34 +30,34 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No projectTemplate found in ES'); - models.ProjectTemplate.findOne({ - where: { - deletedAt: { $eq: null }, - id: req.params.templateId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((projectTemplate) => { - // Not found - if (!projectTemplate) { - const apiErr = new Error(`Project template not found for project id ${req.params.templateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + .then((data) => { + if (data.length === 0) { + req.log.debug('No projectTemplate found in ES'); + models.ProjectTemplate.findOne({ + where: { + deletedAt: { $eq: null }, + id: req.params.templateId, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }) + .then((projectTemplate) => { + // Not found + if (!projectTemplate) { + const apiErr = new Error(`Project template not found for project id ${req.params.templateId}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(projectTemplate); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('projectTemplate found in ES'); - res.json(data[0].inner_hits.projectTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(projectTemplate); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('projectTemplate found in ES'); + res.json(data[0].inner_hits.projectTemplates.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/projectTemplates/list.js b/src/routes/projectTemplates/list.js index ce239861..641a750f 100644 --- a/src/routes/projectTemplates/list.js +++ b/src/routes/projectTemplates/list.js @@ -21,24 +21,24 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.projectTemplates.length === 0) { - req.log.debug('No projectTemplates found in ES'); - models.ProjectTemplate.findAll({ - where: { - deletedAt: { $eq: null }, - disabled: false, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }).then((projectTemplates) => { - res.json(projectTemplates); - }) - .catch(next); - } else { - req.log.debug('projectTemplates found in ES'); - res.json(data.projectTemplates); - } - }); + .then((data) => { + if (data.projectTemplates.length === 0) { + req.log.debug('No projectTemplates found in ES'); + models.ProjectTemplate.findAll({ + where: { + deletedAt: { $eq: null }, + disabled: false, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }).then((projectTemplates) => { + res.json(projectTemplates); + }) + .catch(next); + } else { + req.log.debug('projectTemplates found in ES'); + res.json(data.projectTemplates); + } + }); }, ]; diff --git a/src/routes/projectTemplates/update.js b/src/routes/projectTemplates/update.js index 6661a520..85fa47fb 100644 --- a/src/routes/projectTemplates/update.js +++ b/src/routes/projectTemplates/update.js @@ -50,10 +50,10 @@ const schema = { updatedBy: Joi.any().strip(), deletedBy: Joi.any().strip(), }) - .xor('form', 'scope') - .xor('phases', 'planConfig') - .nand('priceConfig', 'scope') - .required(), + .xor('form', 'scope') + .xor('phases', 'planConfig') + .nand('priceConfig', 'scope') + .required(), }; module.exports = [ @@ -69,19 +69,19 @@ module.exports = [ util.checkModel(priceConfig, 'PriceConfig', models.PriceConfig, 'project template'), util.checkModel(planConfig, 'PlanConfig', models.PlanConfig, 'project template'), ]) - .then(() => { - const entityToUpdate = _.assign(req.body, { - updatedBy: req.authUser.userId, - }); + .then(() => { + const entityToUpdate = _.assign(req.body, { + updatedBy: req.authUser.userId, + }); - return models.ProjectTemplate.findOne({ - where: { - deletedAt: { $eq: null }, - id: req.params.templateId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, + return models.ProjectTemplate.findOne({ + where: { + deletedAt: { $eq: null }, + id: req.params.templateId, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) + }) .then((projectTemplate) => { // Not found if (!projectTemplate) { @@ -112,6 +112,6 @@ module.exports = [ res.json(projectTemplate); }); - }).catch(next); + }).catch(next); }, ]; diff --git a/src/routes/projectTemplates/update.spec.js b/src/routes/projectTemplates/update.spec.js index 85757148..05392281 100644 --- a/src/routes/projectTemplates/update.spec.js +++ b/src/routes/projectTemplates/update.spec.js @@ -54,63 +54,63 @@ describe('UPDATE project template', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => models.ProjectType.bulkCreate([ - { - key: 'generic', - displayName: 'Generic', - icon: 'http://example.com/icon1.ico', - question: 'question 1', - info: 'info 1', - aliases: ['key-1', 'key_1'], - metadata: {}, + .then(() => models.ProjectType.bulkCreate([ + { + key: 'generic', + displayName: 'Generic', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + metadata: {}, + createdBy: 1, + updatedBy: 1, + }, + { + key: 'concrete', + displayName: 'Concrete', + icon: 'http://example.com/icon1.ico', + question: 'question 2', + info: 'info 2', + aliases: ['key-2', 'key_2'], + metadata: {}, + createdBy: 1, + updatedBy: 1, + }, + ])) + .then(() => models.ProjectTemplate.create(template).then((createdTemplate) => { + templateId = createdTemplate.id; + })) + .then(() => models.Form.create({ + key: 'test', + config: { + test: 'test1', + }, + version: 1, + revision: 1, createdBy: 1, updatedBy: 1, - }, - { - key: 'concrete', - displayName: 'Concrete', - icon: 'http://example.com/icon1.ico', - question: 'question 2', - info: 'info 2', - aliases: ['key-2', 'key_2'], - metadata: {}, + })) + .then(() => models.PlanConfig.create({ + key: 'test', + config: { + test: 'test1', + }, + version: 1, + revision: 1, createdBy: 1, updatedBy: 1, - }, - ])) - .then(() => models.ProjectTemplate.create(template).then((createdTemplate) => { - templateId = createdTemplate.id; - })) - .then(() => models.Form.create({ - key: 'test', - config: { - test: 'test1', - }, - version: 1, - revision: 1, - createdBy: 1, - updatedBy: 1, - })) - .then(() => models.PlanConfig.create({ - key: 'test', - config: { - test: 'test1', - }, - version: 1, - revision: 1, - createdBy: 1, - updatedBy: 1, - })) - .then(() => models.PriceConfig.create({ - key: 'test', - config: { - test: 'test1', - }, - version: 1, - revision: 1, - createdBy: 1, - updatedBy: 1, - }).then(() => done())); + })) + .then(() => models.PriceConfig.create({ + key: 'test', + config: { + test: 'test1', + }, + version: 1, + revision: 1, + createdBy: 1, + updatedBy: 1, + }).then(() => done())); }); after((done) => { testUtil.clearDb(done); diff --git a/src/routes/projectTemplates/upgrade.js b/src/routes/projectTemplates/upgrade.js index 4c064d26..c338ed43 100644 --- a/src/routes/projectTemplates/upgrade.js +++ b/src/routes/projectTemplates/upgrade.js @@ -118,6 +118,6 @@ module.exports = [ res.status(201).json(_.omit(newPt.toJSON(), 'deletedAt', 'deletedBy')); }) - .catch(next)); + .catch(next)); }, ]; diff --git a/src/routes/projectTemplates/upgrade.spec.js b/src/routes/projectTemplates/upgrade.spec.js index 28341e8b..4735abe7 100644 --- a/src/routes/projectTemplates/upgrade.spec.js +++ b/src/routes/projectTemplates/upgrade.spec.js @@ -182,10 +182,10 @@ describe('Upgrade project template', () => { it('should return 404 for non-existed template', (done) => { request(server) - .post('/v5/projects/metadata/projectTemplates/123/upgrade') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post('/v5/projects/metadata/projectTemplates/123/upgrade') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect(404, done); }); @@ -194,10 +194,10 @@ describe('Upgrade project template', () => { models.ProjectTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .post(`/v5/projects/metadata/projectTemplates/${templateId}/upgrade`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post(`/v5/projects/metadata/projectTemplates/${templateId}/upgrade`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect(404, done); }); @@ -205,10 +205,10 @@ describe('Upgrade project template', () => { it('should return 200 for admin', (done) => { request(server) - .post(`/v5/projects/metadata/projectTemplates/${templateId}/upgrade`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) + .post(`/v5/projects/metadata/projectTemplates/${templateId}/upgrade`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) .send(body) .expect(200) .end((err, res) => { diff --git a/src/routes/projectTypes/delete.js b/src/routes/projectTypes/delete.js index 35aeb9fd..c199d998 100644 --- a/src/routes/projectTypes/delete.js +++ b/src/routes/projectTypes/delete.js @@ -21,7 +21,7 @@ module.exports = [ validate(schema), permissions('projectType.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.ProjectType.findByPk(req.params.key) .then((entity) => { if (!entity) { @@ -33,13 +33,13 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then((entity) => { - // emit event - util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, - RESOURCES.PROJECT_TYPE, - _.pick(entity.toJSON(), 'key')); - res.status(204).end(); - }) - .catch(next), + .then((entity) => { + // emit event + util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.PROJECT_METADATA_DELETE, + RESOURCES.PROJECT_TYPE, + _.pick(entity.toJSON(), 'key')); + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/projectTypes/delete.spec.js b/src/routes/projectTypes/delete.spec.js index 749dc139..79773117 100644 --- a/src/routes/projectTypes/delete.spec.js +++ b/src/routes/projectTypes/delete.spec.js @@ -10,27 +10,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (key, err, next) => { if (err) throw err; setTimeout(() => - models.ProjectType.findOne({ - where: { - key, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.ProjectType.findOne({ + where: { + key, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/metadata/projectTypes/${key}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/metadata/projectTypes/${key}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE project type', () => { diff --git a/src/routes/projectTypes/get.js b/src/routes/projectTypes/get.js index c203d8f4..e1d682c0 100644 --- a/src/routes/projectTypes/get.js +++ b/src/routes/projectTypes/get.js @@ -30,33 +30,33 @@ module.exports = [ }, }, }, 'metadata') - .then((data) => { - if (data.length === 0) { - req.log.debug('No projectType found in ES'); - models.ProjectType.findOne({ - where: { - key: req.params.key, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((projectType) => { + .then((data) => { + if (data.length === 0) { + req.log.debug('No projectType found in ES'); + models.ProjectType.findOne({ + where: { + key: req.params.key, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + }) + .then((projectType) => { // Not found - if (!projectType) { - const apiErr = new Error(`Project type not found for key ${req.params.key}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!projectType) { + const apiErr = new Error(`Project type not found for key ${req.params.key}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - res.json(projectType); - return Promise.resolve(); - }) - .catch(next); - } else { - req.log.debug('projectTypes found in ES'); - res.json(data[0].inner_hits.projectTypes.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle - } - }) - .catch(next); + res.json(projectType); + return Promise.resolve(); + }) + .catch(next); + } else { + req.log.debug('projectTypes found in ES'); + res.json(data[0].inner_hits.projectTypes.hits.hits[0]._source); // eslint-disable-line no-underscore-dangle + } + }) + .catch(next); }, ]; diff --git a/src/routes/projectTypes/list.js b/src/routes/projectTypes/list.js index f71f11bd..a197d675 100644 --- a/src/routes/projectTypes/list.js +++ b/src/routes/projectTypes/list.js @@ -11,22 +11,22 @@ module.exports = [ permissions('projectType.view'), (req, res, next) => { util.fetchFromES('projectTypes') - .then((data) => { - if (data.projectTypes.length === 0) { - req.log.debug('No projectType found in ES'); - models.ProjectType.findAll({ - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((projectTypes) => { - res.json(projectTypes); + .then((data) => { + if (data.projectTypes.length === 0) { + req.log.debug('No projectType found in ES'); + models.ProjectType.findAll({ + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, }) - .catch(next); - } else { - req.log.debug('projectTypes found in ES'); - res.json(data.projectTypes); - } - }); + .then((projectTypes) => { + res.json(projectTypes); + }) + .catch(next); + } else { + req.log.debug('projectTypes found in ES'); + res.json(data.projectTypes); + } + }); }, ]; diff --git a/src/routes/projects/create.js b/src/routes/projects/create.js index cfeaede6..55e57ab3 100644 --- a/src/routes/projects/create.js +++ b/src/routes/projects/create.js @@ -44,7 +44,7 @@ const createProjectValidations = { updatedBy: Joi.number().integer().positive(), })).optional().allow(null), estimatedPrice: Joi.number().precision(2).positive().optional() - .allow(null), + .allow(null), terms: Joi.array().items(Joi.number().positive()).optional(), external: Joi.object().keys({ id: Joi.string(), @@ -212,11 +212,11 @@ function createProjectAndPhases(req, project, projectTemplate, productTemplates, req.log.debug('creating project estimation items with building blocks'); if (result.estimations && result.estimations.length > 0) { return createEstimationItemsWithBuildingBlock(result.estimations, req.authUser.userId) - .then((estimationItems) => { - req.log.debug(`creating ${estimationItems.length} project estimation items`); - // ignore project estimation items for now - return Promise.resolve(newProject); - }); + .then((estimationItems) => { + req.log.debug(`creating ${estimationItems.length} project estimation items`); + // ignore project estimation items for now + return Promise.resolve(newProject); + }); } return Promise.resolve(newProject); }).then((newProject) => { @@ -235,61 +235,61 @@ function createProjectAndPhases(req, project, projectTemplate, productTemplates, } return Promise.resolve(newProject); }) - .then((newProject) => { - result.newProject = newProject; + .then((newProject) => { + result.newProject = newProject; - // backward compatibility for releasing the service before releasing the front end - if (!projectTemplate) { - return Promise.resolve(result); - } - const productTemplateMap = {}; - productTemplates.forEach((pt) => { - productTemplateMap[pt.id] = pt; - }); + // backward compatibility for releasing the service before releasing the front end + if (!projectTemplate) { + return Promise.resolve(result); + } + const productTemplateMap = {}; + productTemplates.forEach((pt) => { + productTemplateMap[pt.id] = pt; + }); - if (phasesList) { - return Promise.all(_.map(phasesList, (phase, phaseIdx) => { - const duration = _.get(phase, 'duration', 1); - const startDate = moment.utc().hours(0).minutes(0).seconds(0) - .milliseconds(0); - // Create phase - return models.ProjectPhase.create({ - projectId: newProject.id, - name: _.get(phase, 'name', `Stage ${phaseIdx}`), - duration, - startDate: startDate.format(), - endDate: moment.utc(startDate).add(duration - 1, 'days').format(), - status: _.get(phase, 'status', PROJECT_PHASE_STATUS.DRAFT), - budget: _.get(phase, 'budget', 0), - updatedBy: req.authUser.userId, - createdBy: req.authUser.userId, - }).then((newPhase) => { - req.log.debug(`Creating products in the newly created phase ${newPhase.id}`); - // Create products - return models.PhaseProduct.bulkCreate(_.map(phase.products, (product, productIndex) => ({ - phaseId: newPhase.id, + if (phasesList) { + return Promise.all(_.map(phasesList, (phase, phaseIdx) => { + const duration = _.get(phase, 'duration', 1); + const startDate = moment.utc().hours(0).minutes(0).seconds(0) + .milliseconds(0); + // Create phase + return models.ProjectPhase.create({ projectId: newProject.id, - estimatedPrice: _.get(product, 'estimatedPrice', 0), - name: _.get(product, 'name', _.get(productTemplateMap, `${product.id}.name`, `Product ${productIndex}`)), - // assumes that phase template always contains id of each product - templateId: parseInt(product.id, 10), + name: _.get(phase, 'name', `Stage ${phaseIdx}`), + duration, + startDate: startDate.format(), + endDate: moment.utc(startDate).add(duration - 1, 'days').format(), + status: _.get(phase, 'status', PROJECT_PHASE_STATUS.DRAFT), + budget: _.get(phase, 'budget', 0), updatedBy: req.authUser.userId, createdBy: req.authUser.userId, - })), { returning: true }) - .then((products) => { - // Add phases and products to the project JSON, so they can be stored to ES later - const newPhaseJson = _.omit(newPhase.toJSON(), ['deletedAt', 'deletedBy']); - newPhaseJson.products = _.map(products, product => - _.omit(product.toJSON(), ['deletedAt', 'deletedBy'])); - result.newPhases.push(newPhaseJson); - return Promise.resolve(); + }).then((newPhase) => { + req.log.debug(`Creating products in the newly created phase ${newPhase.id}`); + // Create products + return models.PhaseProduct.bulkCreate(_.map(phase.products, (product, productIndex) => ({ + phaseId: newPhase.id, + projectId: newProject.id, + estimatedPrice: _.get(product, 'estimatedPrice', 0), + name: _.get(product, 'name', _.get(productTemplateMap, `${product.id}.name`, `Product ${productIndex}`)), + // assumes that phase template always contains id of each product + templateId: parseInt(product.id, 10), + updatedBy: req.authUser.userId, + createdBy: req.authUser.userId, + })), { returning: true }) + .then((products) => { + // Add phases and products to the project JSON, so they can be stored to ES later + const newPhaseJson = _.omit(newPhase.toJSON(), ['deletedAt', 'deletedBy']); + newPhaseJson.products = _.map(products, product => + _.omit(product.toJSON(), ['deletedAt', 'deletedBy'])); + result.newPhases.push(newPhaseJson); + return Promise.resolve(); + }); }); - }); - })); - } - return Promise.resolve(); - }) - .then(() => Promise.resolve(result)); + })); + } + return Promise.resolve(); + }) + .then(() => Promise.resolve(result)); } /** @@ -303,77 +303,77 @@ function validateAndFetchTemplates(templateId) { // we ignore missing template id field and create a project without phase/products if (!templateId) return Promise.resolve({}); return models.ProjectTemplate.findByPk(templateId, { raw: true }) - .then((existingProjectTemplate) => { - if (!existingProjectTemplate) { + .then((existingProjectTemplate) => { + if (!existingProjectTemplate) { // Not found - const apiErr = new Error(`Project template not found for id ${templateId}`); - apiErr.status = 400; - return Promise.reject(apiErr); - } - return Promise.resolve(existingProjectTemplate); - }) - .then((projectTemplate) => { + const apiErr = new Error(`Project template not found for id ${templateId}`); + apiErr.status = 400; + return Promise.reject(apiErr); + } + return Promise.resolve(existingProjectTemplate); + }) + .then((projectTemplate) => { // for old projectTemplate with `phases` just get phases config directly from projectTemplate - if (projectTemplate.phases) { + if (projectTemplate.phases) { // for now support both ways: creating phases and creating workstreams - const phasesList = _(projectTemplate.phases).omit('workstreamsConfig').values().value(); - const workstreamsConfig = _.get(projectTemplate.phases, 'workstreamsConfig'); + const phasesList = _(projectTemplate.phases).omit('workstreamsConfig').values().value(); + const workstreamsConfig = _.get(projectTemplate.phases, 'workstreamsConfig'); - return { projectTemplate, phasesList, workstreamsConfig }; - } + return { projectTemplate, phasesList, workstreamsConfig }; + } - // for new projectTemplates try to get phases from the `planConfig`, if it's defined - if (projectTemplate.planConfig) { - return models.PlanConfig.findOneWithLatestRevision(projectTemplate.planConfig).then((planConfig) => { - if (!planConfig) { - const apiErr = new Error(`Cannot find planConfig ${JSON.stringify(projectTemplate.planConfig)}`); - apiErr.status = 400; - throw apiErr; - } + // for new projectTemplates try to get phases from the `planConfig`, if it's defined + if (projectTemplate.planConfig) { + return models.PlanConfig.findOneWithLatestRevision(projectTemplate.planConfig).then((planConfig) => { + if (!planConfig) { + const apiErr = new Error(`Cannot find planConfig ${JSON.stringify(projectTemplate.planConfig)}`); + apiErr.status = 400; + throw apiErr; + } - // for now support both ways: creating phases and creating workstreams - const phasesList = _(planConfig.config).omit('workstreamsConfig').values().value(); - const workstreamsConfig = _.get(planConfig.config, 'workstreamsConfig'); + // for now support both ways: creating phases and creating workstreams + const phasesList = _(planConfig.config).omit('workstreamsConfig').values().value(); + const workstreamsConfig = _.get(planConfig.config, 'workstreamsConfig'); - return { projectTemplate, phasesList, workstreamsConfig }; - }); - } + return { projectTemplate, phasesList, workstreamsConfig }; + }); + } - return { projectTemplate }; - }) - .then(({ projectTemplate, phasesList, workstreamsConfig }) => { - const productPromises = []; - if (phasesList) { - phasesList.forEach((phase) => { + return { projectTemplate }; + }) + .then(({ projectTemplate, phasesList, workstreamsConfig }) => { + const productPromises = []; + if (phasesList) { + phasesList.forEach((phase) => { // Make sure number of products of per phase <= max value - const productCount = _.isArray(phase.products) ? phase.products.length : 0; - if (productCount > config.maxPhaseProductCount) { - const apiErr = new Error(`Number of products per phase cannot exceed ${config.maxPhaseProductCount}`); - apiErr.status = 400; - throw apiErr; - } - _.map(phase.products, (product) => { - productPromises.push(models.ProductTemplate.findByPk(product.id) - .then((productTemplate) => { - if (!productTemplate) { - // Not found - const apiErr = new Error(`Product template not found for id ${product.id}`); - apiErr.status = 400; - return Promise.reject(apiErr); - } - return Promise.resolve(productTemplate); - })); + const productCount = _.isArray(phase.products) ? phase.products.length : 0; + if (productCount > config.maxPhaseProductCount) { + const apiErr = new Error(`Number of products per phase cannot exceed ${config.maxPhaseProductCount}`); + apiErr.status = 400; + throw apiErr; + } + _.map(phase.products, (product) => { + productPromises.push(models.ProductTemplate.findByPk(product.id) + .then((productTemplate) => { + if (!productTemplate) { + // Not found + const apiErr = new Error(`Product template not found for id ${product.id}`); + apiErr.status = 400; + return Promise.reject(apiErr); + } + return Promise.resolve(productTemplate); + })); + }); }); - }); - } - if (productPromises.length > 0) { - return Promise.all(productPromises).then(productTemplates => ( - { projectTemplate, productTemplates, phasesList, workstreamsConfig } - )); - } - // if there is no phase or product in a phase is specified, return empty product templates - return Promise.resolve({ projectTemplate, productTemplates: [], phasesList, workstreamsConfig }); - }); + } + if (productPromises.length > 0) { + return Promise.all(productPromises).then(productTemplates => ( + { projectTemplate, productTemplates, phasesList, workstreamsConfig } + )); + } + // if there is no phase or product in a phase is specified, return empty product templates + return Promise.resolve({ projectTemplate, productTemplates: [], phasesList, workstreamsConfig }); + }); } module.exports = [ @@ -381,7 +381,7 @@ module.exports = [ validate(createProjectValidations), permissions('project.create'), fieldLookupValidation(models.ProjectType, 'key', 'body.type', 'Project type'), - /** + /* * POST projects/ * Create a project if the user has access */ @@ -447,75 +447,75 @@ module.exports = [ // Validate the templates return validateAndFetchTemplates(project.templateId) // Create project and phases - .then(({ projectTemplate, productTemplates, phasesList, workstreamsConfig }) => { - req.log.debug('Creating project, phase and products'); - // only if workstream config is provided, treat such project as using workstreams - // otherwise project would still use phases - if (workstreamsConfig) { - _.set(project, 'details.settings.workstreams', true); - } - return createProjectAndPhases(req, project, projectTemplate, productTemplates, phasesList) - .then(createdProjectAndPhases => - createWorkstreams(req, createdProjectAndPhases.newProject, workstreamsConfig) - .then(() => createdProjectAndPhases), - ); - }) - .then((createdProjectAndPhases) => { - newProject = createdProjectAndPhases.newProject; - newPhases = createdProjectAndPhases.newPhases; - projectEstimations = createdProjectAndPhases.estimations; - projectAttachments = createdProjectAndPhases.attachments; - - req.log.debug('new project created (id# %d, name: %s)', newProject.id, newProject.name); - // create direct project with name and description - const body = { - projectName: newProject.name, - projectDescription: newProject.description, - }; - // billingAccountId is optional field - if (newProject.billingAccountId) { - body.billingAccountId = newProject.billingAccountId; - } - req.log.debug('creating project history for project %d', newProject.id); - // add to project history asynchronously, don't wait for it to complete - models.ProjectHistory.create({ - projectId: newProject.id, - status: PROJECT_STATUS.IN_REVIEW, - cancelReason: null, - updatedBy: req.authUser.userId, - }).then(() => req.log.debug('project history created for project %d', newProject.id)) - .catch(() => req.log.error('project history failed for project %d', newProject.id)); - return Promise.resolve(); - }); - }) - .then(() => { - newProject = newProject.get({ plain: true }); - // remove utm details & deletedAt field - newProject = _.omit(newProject, ['deletedAt', 'utm']); - // add the project attachments, if any - newProject.attachments = projectAttachments; - // set phases array - newProject.phases = newPhases; - // sets estimations array - if (projectEstimations) { - newProject.estimations = projectEstimations; - } + .then(({ projectTemplate, productTemplates, phasesList, workstreamsConfig }) => { + req.log.debug('Creating project, phase and products'); + // only if workstream config is provided, treat such project as using workstreams + // otherwise project would still use phases + if (workstreamsConfig) { + _.set(project, 'details.settings.workstreams', true); + } + return createProjectAndPhases(req, project, projectTemplate, productTemplates, phasesList) + .then(createdProjectAndPhases => + createWorkstreams(req, createdProjectAndPhases.newProject, workstreamsConfig) + .then(() => createdProjectAndPhases), + ); + }) + .then((createdProjectAndPhases) => { + newProject = createdProjectAndPhases.newProject; + newPhases = createdProjectAndPhases.newPhases; + projectEstimations = createdProjectAndPhases.estimations; + projectAttachments = createdProjectAndPhases.attachments; - req.log.debug('Sending event to RabbitMQ bus for project %d', newProject.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED, - newProject, - { correlationId: req.id }, - ); - req.log.debug('Sending event to Kafka bus for project %d', newProject.id); - // emit event - req.app.emit(EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED, - { req, project: _.assign({ resource: RESOURCES.PROJECT }, newProject), + req.log.debug('new project created (id# %d, name: %s)', newProject.id, newProject.name); + // create direct project with name and description + const body = { + projectName: newProject.name, + projectDescription: newProject.description, + }; + // billingAccountId is optional field + if (newProject.billingAccountId) { + body.billingAccountId = newProject.billingAccountId; + } + req.log.debug('creating project history for project %d', newProject.id); + // add to project history asynchronously, don't wait for it to complete + models.ProjectHistory.create({ + projectId: newProject.id, + status: PROJECT_STATUS.IN_REVIEW, + cancelReason: null, + updatedBy: req.authUser.userId, + }).then(() => req.log.debug('project history created for project %d', newProject.id)) + .catch(() => req.log.error('project history failed for project %d', newProject.id)); + return Promise.resolve(); }); - res.status(201).json(newProject); }) - .catch((err) => { - req.log.error(err.message); - util.handleError('Error creating project', err, req, next); - }); + .then(() => { + newProject = newProject.get({ plain: true }); + // remove utm details & deletedAt field + newProject = _.omit(newProject, ['deletedAt', 'utm']); + // add the project attachments, if any + newProject.attachments = projectAttachments; + // set phases array + newProject.phases = newPhases; + // sets estimations array + if (projectEstimations) { + newProject.estimations = projectEstimations; + } + + req.log.debug('Sending event to RabbitMQ bus for project %d', newProject.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED, + newProject, + { correlationId: req.id }, + ); + req.log.debug('Sending event to Kafka bus for project %d', newProject.id); + // emit event + req.app.emit(EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED, + { req, project: _.assign({ resource: RESOURCES.PROJECT }, newProject), + }); + res.status(201).json(newProject); + }) + .catch((err) => { + req.log.error(err.message); + util.handleError('Error creating project', err, req, next); + }); }, ]; diff --git a/src/routes/projects/delete.js b/src/routes/projects/delete.js index bdbf7b03..8ae66a92 100644 --- a/src/routes/projects/delete.js +++ b/src/routes/projects/delete.js @@ -28,18 +28,18 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(project => project.destroy({ cascade: true }))) - .then((project) => { - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_DELETED, - { id: projectId }, - { correlationId: req.id }, - ); - // emit event - req.app.emit(EVENT.ROUTING_KEY.PROJECT_DELETED, - { req, project: _.assign({ resource: RESOURCES.PROJECT }, _.pick(project.toJSON(), 'id')), - }); - res.status(204).json({}); - }) - .catch(err => next(err)); + .then((project) => { + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_DELETED, + { id: projectId }, + { correlationId: req.id }, + ); + // emit event + req.app.emit(EVENT.ROUTING_KEY.PROJECT_DELETED, + { req, project: _.assign({ resource: RESOURCES.PROJECT }, _.pick(project.toJSON(), 'id')), + }); + res.status(204).json({}); + }) + .catch(err => next(err)); }, ]; diff --git a/src/routes/projects/delete.spec.js b/src/routes/projects/delete.spec.js index 20dbea78..b1a46db3 100644 --- a/src/routes/projects/delete.spec.js +++ b/src/routes/projects/delete.spec.js @@ -9,28 +9,28 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (id, err, next) => { if (err) throw err; setTimeout(() => - models.Project.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.Project.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404) - .end(next); - } - }), 500); + request(server) + .get(`/v5/projects/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404) + .end(next); + } + }), 500); }; describe('Project delete test', () => { let project1; diff --git a/src/routes/projects/get.js b/src/routes/projects/get.js index 148601b2..ff9416e2 100644 --- a/src/routes/projects/get.js +++ b/src/routes/projects/get.js @@ -76,7 +76,7 @@ const parseElasticSearchCriteria = (projectId, fields) => { } if (sourceInclude) { - searchCriteria._sourceIncludes = sourceInclude; // eslint-disable-line no-underscore-dangle + searchCriteria._sourceIncludes = sourceInclude; // eslint-disable-line no-underscore-dangle } @@ -114,7 +114,7 @@ const retrieveProjectFromES = (projectId, req) => { return new Promise((accept, reject) => { const es = util.getElasticSearchClient(); es.search(searchCriteria).then((docs) => { - const rows = _.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle + const rows = _.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle accept(rows[0]); }).catch(reject); }); @@ -137,43 +137,43 @@ const retrieveProjectFromDB = (projectId, req) => { }).then((_project) => { project = _project; if (!project) { - // returning 404 + // returning 404 const apiErr = new Error(`project not found for id ${projectId}`); apiErr.status = 404; return Promise.reject(apiErr); } - // check context for project members + // check context for project members project.members = _.map(req.context.currentProjectMembers, m => _.pick(m, fields.project_members)); - // check if attachments field was requested + // check if attachments field was requested if (!req.query.fields || _.indexOf(req.query.fields, 'attachments') > -1) { return util.getProjectAttachments(req, project.id); } - // return null if attachments were not requested. + // return null if attachments were not requested. return Promise.resolve(null); }) - .then((attachments) => { - // if attachments were requested - if (attachments) { - project.attachments = attachments; - } - return models.ProjectMemberInvite.getPendingAndReguestedInvitesForProject(projectId); - }) - .then((invites) => { - project.invites = invites; - return project; - }); + .then((attachments) => { + // if attachments were requested + if (attachments) { + project.attachments = attachments; + } + return models.ProjectMemberInvite.getPendingAndReguestedInvitesForProject(projectId); + }) + .then((invites) => { + project.invites = invites; + return project; + }); }; module.exports = [ permissions('project.view'), - /** + /* * GET projects/{projectId} * Get a project by id */ (req, res, next) => { const projectId = Number(req.params.projectId); - // parse the fields string to determine what fields are to be returned + // parse the fields string to determine what fields are to be returned return retrieveProjectFromES(projectId, req).then((result) => { if (result === undefined) { diff --git a/src/routes/projects/get.spec.js b/src/routes/projects/get.spec.js index a12e46f6..79681934 100644 --- a/src/routes/projects/get.spec.js +++ b/src/routes/projects/get.spec.js @@ -115,81 +115,81 @@ describe('GET Project', () => { let project2; before((done) => { testUtil.clearDb() - .then(() => testUtil.clearES()) - .then(() => { - const p1 = models.Project.create({ - id: 5, - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - details: {}, + .then(() => testUtil.clearES()) + .then(() => { + const p1 = models.Project.create({ + id: 5, + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + const pm1 = models.ProjectMember.create({ + userId: 40051331, + projectId: project1.id, + role: 'customer', + isPrimary: true, + firstName: 'Firstname', + lastName: 'Lastname', + handle: 'test_tourist_handle', + email: 'test@test.com', + createdBy: 1, + updatedBy: 1, + }); + const pm2 = models.ProjectMember.create({ + userId: 40051333, + projectId: project1.id, + role: 'copilot', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project1 = p; - // create members - const pm1 = models.ProjectMember.create({ - userId: 40051331, - projectId: project1.id, - role: 'customer', - isPrimary: true, - firstName: 'Firstname', - lastName: 'Lastname', - handle: 'test_tourist_handle', - email: 'test@test.com', - createdBy: 1, - updatedBy: 1, - }); - const pm2 = models.ProjectMember.create({ - userId: 40051333, - projectId: project1.id, - role: 'copilot', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }); - return Promise.all([pm1, pm2]); }); + return Promise.all([pm1, pm2]); + }); - const p2 = models.Project.create({ - type: 'visual_design', - billingAccountId: 1, - name: 'test2', - description: 'db_project', - id: 2, - status: 'draft', - details: {}, + const p2 = models.Project.create({ + type: 'visual_design', + billingAccountId: 1, + name: 'test2', + description: 'db_project', + id: 2, + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project2 = p; + return models.ProjectMember.create({ + userId: 40051335, + projectId: project2.id, + role: 'manager', + handle: 'manager_handle', + isPrimary: true, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }).then((p) => { - project2 = p; - return models.ProjectMember.create({ - userId: 40051335, - projectId: project2.id, - role: 'manager', - handle: 'manager_handle', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }); }); - return Promise.all([p1, p2]) - .then(() => server.services.es.index({ - index: ES_PROJECT_INDEX, - type: ES_PROJECT_TYPE, - id: data[0].id, - body: data[0], - })).then(() => { - testUtil.wait(done); - // done(); - }); }); + return Promise.all([p1, p2]) + .then(() => server.services.es.index({ + index: ES_PROJECT_INDEX, + type: ES_PROJECT_TYPE, + id: data[0].id, + body: data[0], + })).then(() => { + testUtil.wait(done); + // done(); + }); + }); }); after((done) => { @@ -201,34 +201,186 @@ describe('GET Project', () => { describe('GET /projects/{id}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get(`/v5/projects/${project2.id}`) - .expect(403) - .end(done); + .get(`/v5/projects/${project2.id}`) + .expect(403) + .end(done); }); it('should return 404 if requested project doesn\'t exist', (done) => { request(server) - .get('/v5/projects/14343323') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404) - .end(done); + .get('/v5/projects/14343323') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404) + .end(done); }); it('should return 403 if user does not have access to the project', (done) => { request(server) - .get(`/v5/projects/${project2.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect(403) - .end(done); + .get(`/v5/projects/${project2.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(403) + .end(done); }); it('should return the project when registerd member attempts to access the project', (done) => { request(server) - .get(`/v5/projects/${project1.id}/?fields=id%2Cname%2Cstatus%2Cmembers.role%2Cmembers.id%2Cmembers.userId`) + .get(`/v5/projects/${project1.id}/?fields=id%2Cname%2Cstatus%2Cmembers.role%2Cmembers.id%2Cmembers.userId`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + should.not.exist(resJson.deletedAt); + should.not.exist(resJson.billingAccountId); + should.exist(resJson.name); + resJson.status.should.be.eql('draft'); + resJson.members.should.have.lengthOf(2); + done(); + } + }); + }); + + it('should return the project using M2M token with "read:projects" scope', (done) => { + request(server) + .get(`/v5/projects/${project1.id}/?fields=id%2Cname%2Cstatus%2Cmembers.role%2Cmembers.id%2Cmembers.userId`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:projects']}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + should.not.exist(resJson.deletedAt); + should.not.exist(resJson.billingAccountId); + should.exist(resJson.name); + resJson.status.should.be.eql('draft'); + resJson.members.should.have.lengthOf(2); + done(); + } + }); + }); + + it('should return project with "members", "invites", and "attachments" by default when data comes from ES', (done) => { + request(server) + .get(`/v5/projects/${data[0].id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.description.should.be.eql('es_project'); + resJson.members.should.have.lengthOf(2); + resJson.members[0].firstName.should.equal('es_member_1_firstName'); + + resJson.attachments.should.have.lengthOf(2); + resJson.attachments[0].id.should.eql(data[0].attachments[0].id); + resJson.attachments[0].title.should.eql(data[0].attachments[0].title); + resJson.attachments[0].projectId.should.eql(data[0].attachments[0].projectId); + resJson.attachments[0].description.should.eql(data[0].attachments[0].description); + resJson.attachments[0].path.should.eql(data[0].attachments[0].path); + resJson.attachments[0].tags.should.eql(data[0].attachments[0].tags); + resJson.attachments[0].contentType.should.eql(data[0].attachments[0].contentType); + resJson.attachments[0].createdBy.should.eql(data[0].attachments[0].createdBy); + resJson.attachments[0].updatedBy.should.eql(data[0].attachments[0].updatedBy); + + resJson.attachments[1].id.should.eql(data[0].attachments[1].id); + resJson.attachments[1].title.should.eql(data[0].attachments[1].title); + resJson.attachments[1].projectId.should.eql(data[0].attachments[1].projectId); + resJson.attachments[1].description.should.eql(data[0].attachments[1].description); + resJson.attachments[1].path.should.eql(data[0].attachments[1].path); + resJson.attachments[1].tags.should.eql(data[0].attachments[1].tags); + resJson.attachments[1].createdBy.should.eql(data[0].attachments[1].createdBy); + resJson.attachments[1].updatedBy.should.eql(data[0].attachments[1].updatedBy); + + done(); + } + }); + }); + + it('should return project with "members", "invites", and "attachments" by default when data comes from DB', (done) => { + request(server) + .get(`/v5/projects/${project2.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.description.should.be.eql('db_project'); + resJson.members.should.have.lengthOf(1); + resJson.members[0].role.should.equal('manager'); + done(); + } + }); + }); + + it('should return the project for administrator ', (done) => { + request(server) + .get(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + done(); + } + }); + }); + + it('should return the project for connect admin ', (done) => { + request(server) + .get(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + done(); + } + }); + }); + + describe('URL Query fields', () => { + it('should not return "email" for project members when "fields" query param is not defined (to non-admin users)', (done) => { + request(server) + .get(`/v5/projects/${project1.id}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -240,21 +392,18 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - should.not.exist(resJson.deletedAt); - should.not.exist(resJson.billingAccountId); - should.exist(resJson.name); - resJson.status.should.be.eql('draft'); - resJson.members.should.have.lengthOf(2); + resJson.members[0].should.have.property('handle'); + resJson.members[0].should.not.have.property('email'); done(); } }); - }); + }); - it('should return the project using M2M token with "read:projects" scope', (done) => { - request(server) - .get(`/v5/projects/${project1.id}/?fields=id%2Cname%2Cstatus%2Cmembers.role%2Cmembers.id%2Cmembers.userId`) + it('should not return "email" for project members even if it\'s listed in "fields" query param (to non-admin users)', (done) => { + request(server) + .get(`/v5/projects/${project1.id}?fields=members.email,members.handle`) .set({ - Authorization: `Bearer ${testUtil.m2m['read:projects']}`, + Authorization: `Bearer ${testUtil.jwts.member}`, }) .expect('Content-Type', /json/) .expect(200) @@ -264,21 +413,19 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - should.not.exist(resJson.deletedAt); - should.not.exist(resJson.billingAccountId); - should.exist(resJson.name); - resJson.status.should.be.eql('draft'); - resJson.members.should.have.lengthOf(2); + resJson.members[0].should.have.property('handle'); + resJson.members[0].should.not.have.property('email'); done(); } }); - }); + }); - it('should return project with "members", "invites", and "attachments" by default when data comes from ES', (done) => { - request(server) - .get(`/v5/projects/${data[0].id}`) + + it('should not return "cancelReason" if it is not listed in "fields" query param ', (done) => { + request(server) + .get(`/v5/projects/${project1.id}?fields=description`) .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, + Authorization: `Bearer ${testUtil.jwts.member}`, }) .expect('Content-Type', /json/) .expect(200) @@ -288,38 +435,17 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - resJson.description.should.be.eql('es_project'); - resJson.members.should.have.lengthOf(2); - resJson.members[0].firstName.should.equal('es_member_1_firstName'); - - resJson.attachments.should.have.lengthOf(2); - resJson.attachments[0].id.should.eql(data[0].attachments[0].id); - resJson.attachments[0].title.should.eql(data[0].attachments[0].title); - resJson.attachments[0].projectId.should.eql(data[0].attachments[0].projectId); - resJson.attachments[0].description.should.eql(data[0].attachments[0].description); - resJson.attachments[0].path.should.eql(data[0].attachments[0].path); - resJson.attachments[0].tags.should.eql(data[0].attachments[0].tags); - resJson.attachments[0].contentType.should.eql(data[0].attachments[0].contentType); - resJson.attachments[0].createdBy.should.eql(data[0].attachments[0].createdBy); - resJson.attachments[0].updatedBy.should.eql(data[0].attachments[0].updatedBy); - - resJson.attachments[1].id.should.eql(data[0].attachments[1].id); - resJson.attachments[1].title.should.eql(data[0].attachments[1].title); - resJson.attachments[1].projectId.should.eql(data[0].attachments[1].projectId); - resJson.attachments[1].description.should.eql(data[0].attachments[1].description); - resJson.attachments[1].path.should.eql(data[0].attachments[1].path); - resJson.attachments[1].tags.should.eql(data[0].attachments[1].tags); - resJson.attachments[1].createdBy.should.eql(data[0].attachments[1].createdBy); - resJson.attachments[1].updatedBy.should.eql(data[0].attachments[1].updatedBy); - + resJson.should.have.property('description'); + resJson.description.should.be.eq('es_project'); + resJson.should.not.have.property('cancelReason'); done(); } }); - }); + }); - it('should return project with "members", "invites", and "attachments" by default when data comes from DB', (done) => { - request(server) - .get(`/v5/projects/${project2.id}`) + it('should not return "email" for project members if it\'s not listed in "fields" query param (to admin users)', (done) => { + request(server) + .get(`/v5/projects/${project1.id}?fields=description,members.id`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -331,17 +457,16 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - resJson.description.should.be.eql('db_project'); - resJson.members.should.have.lengthOf(1); - resJson.members[0].role.should.equal('manager'); + resJson.members.should.have.lengthOf(2); + resJson.members[0].should.not.have.property('email'); done(); } }); - }); + }); - it('should return the project for administrator ', (done) => { - request(server) - .get(`/v5/projects/${project1.id}`) + it('should return "email" for project members if it\'s listed in "fields" query param (to admin users)', (done) => { + request(server) + .get(`/v5/projects/${project1.id}?fields=description,members.id,members.email`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -353,16 +478,19 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); + resJson.members.should.have.lengthOf(2); + resJson.members[0].should.have.property('email'); + resJson.members[0].email.should.be.eq('test@test.com'); done(); } }); - }); + }); - it('should return the project for connect admin ', (done) => { - request(server) - .get(`/v5/projects/${project1.id}`) + it('should only return "id" field, when it\'s the only field listed in "fields" query param', (done) => { + request(server) + .get(`/v5/projects/${project1.id}?fields=id`) .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect('Content-Type', /json/) .expect(200) @@ -372,80 +500,16 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); + resJson.should.have.property('id'); + _.keys(resJson).length.should.be.eq(1); done(); } }); - }); - - describe('URL Query fields', () => { - it('should not return "email" for project members when "fields" query param is not defined (to non-admin users)', (done) => { - request(server) - .get(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.members[0].should.have.property('handle'); - resJson.members[0].should.not.have.property('email'); - done(); - } - }); - }); - - it('should not return "email" for project members even if it\'s listed in "fields" query param (to non-admin users)', (done) => { - request(server) - .get(`/v5/projects/${project1.id}?fields=members.email,members.handle`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.members[0].should.have.property('handle'); - resJson.members[0].should.not.have.property('email'); - done(); - } - }); }); - - it('should not return "cancelReason" if it is not listed in "fields" query param ', (done) => { - request(server) - .get(`/v5/projects/${project1.id}?fields=description`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.property('description'); - resJson.description.should.be.eq('es_project'); - resJson.should.not.have.property('cancelReason'); - done(); - } - }); - }); - - it('should not return "email" for project members if it\'s not listed in "fields" query param (to admin users)', (done) => { + it('should only return "invites.userId" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=description,members.id`) + .get(`/v5/projects/${project1.id}?fields=invites.userId`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -457,18 +521,18 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - resJson.members.should.have.lengthOf(2); - resJson.members[0].should.not.have.property('email'); + resJson.invites[0].should.have.property('userId'); + _.keys(resJson.invites[0]).length.should.be.eq(1); done(); } }); }); - it('should return "email" for project members if it\'s listed in "fields" query param (to admin users)', (done) => { + it('should not return "userId" for any invite which has "email" field', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=description,members.id,members.email`) + .get(`/v5/projects/${project1.id}`) .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, + Authorization: `Bearer ${testUtil.jwts.member}`, }) .expect('Content-Type', /json/) .expect(200) @@ -478,160 +542,96 @@ describe('GET Project', () => { } else { const resJson = res.body; should.exist(resJson); - resJson.members.should.have.lengthOf(2); - resJson.members[0].should.have.property('email'); - resJson.members[0].email.should.be.eq('test@test.com'); + resJson.invites.length.should.be.eql(1); + resJson.invites[0].should.have.property('email'); + should.not.exist(resJson.invites[0].userId); done(); } }); }); - it('should only return "id" field, when it\'s the only field listed in "fields" query param', (done) => { - request(server) - .get(`/v5/projects/${project1.id}?fields=id`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.property('id'); - _.keys(resJson).length.should.be.eq(1); - done(); - } - }); - }); - - it('should only return "invites.userId" field, when it\'s the only field listed in "fields" query param', (done) => { - request(server) - .get(`/v5/projects/${project1.id}?fields=invites.userId`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.invites[0].should.have.property('userId'); - _.keys(resJson.invites[0]).length.should.be.eq(1); - done(); - } - }); - }); - - it('should not return "userId" for any invite which has "email" field', (done) => { - request(server) - .get(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.invites.length.should.be.eql(1); - resJson.invites[0].should.have.property('email'); - should.not.exist(resJson.invites[0].userId); - done(); - } - }); - }); - it('should only return "members.role" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=members.role`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.members[0].should.have.property('role'); - _.keys(resJson.members[0]).length.should.be.eq(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}?fields=members.role`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.members[0].should.have.property('role'); + _.keys(resJson.members[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "attachments.title" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=attachments.title`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.attachments[0].should.have.property('title'); - _.keys(resJson.attachments[0]).length.should.be.eq(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}?fields=attachments.title`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.attachments[0].should.have.property('title'); + _.keys(resJson.attachments[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "phases.name" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=phases.name`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.phases[0].should.have.property('name'); - _.keys(resJson.phases[0]).length.should.be.eq(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}?fields=phases.name`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.phases[0].should.have.property('name'); + _.keys(resJson.phases[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "phases.products.name" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get(`/v5/projects/${project1.id}?fields=phases.products.name`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.phases[0].products[0].should.have.property('name'); - _.keys(resJson.phases[0].products[0]).length.should.be.eq(1); - done(); - } - }); + .get(`/v5/projects/${project1.id}?fields=phases.products.name`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.phases[0].products[0].should.have.property('name'); + _.keys(resJson.phases[0].products[0]).length.should.be.eq(1); + done(); + } + }); }); }); }); diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index a9ea8445..68e23a41 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -21,8 +21,8 @@ const MATCH_TYPE_SINGLE_FIELD = 3; * */ const PROJECT_ATTRIBUTES = _.without(_.keys(models.Project.rawAttributes), - 'utm', - 'deletedAt', + 'utm', + 'deletedAt', ); const PROJECT_MEMBER_ATTRIBUTES = _.without(_.keys(models.ProjectMember.rawAttributes)); // project members has some additional fields stored in ES index, which we don't have in DB @@ -321,7 +321,7 @@ const parseElasticSearchCriteria = (criteria, fields, order) => { } if (sourceInclude) { - searchCriteria._sourceIncludes = sourceInclude; // eslint-disable-line no-underscore-dangle + searchCriteria._sourceIncludes = sourceInclude; // eslint-disable-line no-underscore-dangle } // prepare the elasticsearch filter criteria const boolQuery = []; @@ -483,7 +483,7 @@ const retrieveProjectsFromDB = (req, criteria, sort, ffields) => { // order by const order = sort ? [sort.split(' ')] : [['createdAt', 'asc']]; let fields = ffields ? ffields.split(',') : []; - // parse the fields string to determine what fields are to be returned + // parse the fields string to determine what fields are to be returned fields = util.parseFields(fields, { projects: PROJECT_ATTRIBUTES, project_members: PROJECT_MEMBER_ATTRIBUTES, @@ -502,53 +502,53 @@ const retrieveProjectsFromDB = (req, criteria, sort, ffields) => { offset: criteria.offset, attributes: _.get(fields, 'projects', null), }, req.log) - .then(({ rows, count }) => { - const projectIds = _.map(rows, 'id'); - const promises = []; - // retrieve members - if (projectIds.length && retrieveMembers) { - promises.push( - models.ProjectMember.findAll({ - attributes: _.get(fields, 'ProjectMembers'), - where: { projectId: { $in: projectIds } }, - raw: true, - }), - ); - } - if (projectIds.length && retrieveAttachments) { - promises.push( - models.ProjectAttachment.findAll({ - attributes: PROJECT_ATTACHMENT_ATTRIBUTES, - where: { projectId: { $in: projectIds } }, - raw: true, - }), - ); - } - // return results after promise(s) have resolved - return Promise.all(promises) - .then((values) => { - const allMembers = retrieveMembers ? values.shift() : []; - const allAttachments = retrieveAttachments ? values.shift() : []; - _.forEach(rows, (fp) => { - const p = fp; - // if values length is 1 it could be either attachments or members - if (retrieveMembers) { - p.members = _.filter(allMembers, m => m.projectId === p.id); - } - if (retrieveAttachments) { - p.attachments = _.filter(allAttachments, a => a.projectId === p.id); - } + .then(({ rows, count }) => { + const projectIds = _.map(rows, 'id'); + const promises = []; + // retrieve members + if (projectIds.length && retrieveMembers) { + promises.push( + models.ProjectMember.findAll({ + attributes: _.get(fields, 'ProjectMembers'), + where: { projectId: { $in: projectIds } }, + raw: true, + }), + ); + } + if (projectIds.length && retrieveAttachments) { + promises.push( + models.ProjectAttachment.findAll({ + attributes: PROJECT_ATTACHMENT_ATTRIBUTES, + where: { projectId: { $in: projectIds } }, + raw: true, + }), + ); + } + // return results after promise(s) have resolved + return Promise.all(promises) + .then((values) => { + const allMembers = retrieveMembers ? values.shift() : []; + const allAttachments = retrieveAttachments ? values.shift() : []; + _.forEach(rows, (fp) => { + const p = fp; + // if values length is 1 it could be either attachments or members + if (retrieveMembers) { + p.members = _.filter(allMembers, m => m.projectId === p.id); + } + if (retrieveAttachments) { + p.attachments = _.filter(allAttachments, a => a.projectId === p.id); + } + }); + return { rows, count, pageSize: criteria.limit, page: criteria.page }; }); - return { rows, count, pageSize: criteria.limit, page: criteria.page }; - }); - }); + }); }; const retrieveProjects = (req, criteria, sort, ffields) => { // order by const order = sort ? sort.split(' ') : ['createdAt', 'asc']; let fields = ffields ? ffields.split(',') : []; - // parse the fields string to determine what fields are to be returned + // parse the fields string to determine what fields are to be returned fields = util.parseFields(fields, { projects: PROJECT_ATTRIBUTES, project_members: util.addUserDetailsFieldsIfAllowed(PROJECT_MEMBER_ATTRIBUTES_ES, req), @@ -567,14 +567,14 @@ const retrieveProjects = (req, criteria, sort, ffields) => { return new Promise((accept, reject) => { const es = util.getElasticSearchClient(); es.search(searchCriteria).then((docs) => { - const rows = _.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle + const rows = _.map(docs.hits.hits, single => single._source); // eslint-disable-line no-underscore-dangle accept({ rows, count: docs.hits.total, pageSize: criteria.limit, page: criteria.page }); }).catch(reject); }); }; module.exports = [ - /** + /* * GET projects/ * Return a list of projects that match the criteria */ @@ -616,28 +616,28 @@ module.exports = [ if (!memberOnly && util.hasPermission(PERMISSION.READ_PROJECT_ANY, req.authUser)) { // admins & topcoder managers can see all projects return retrieveProjects(req, criteria, sort, req.query.fields) - .then((result) => { - if (result.rows.length === 0) { - req.log.debug('No projects found in ES'); - - // if we have some filters and didn't get any data from ES - // we don't fallback to DB, because DB doesn't support all of the filters - // so we don't want DB to return unrelated data, ref issue #450 - if (_.intersection(_.keys(filters), SUPPORTED_FILTERS).length > 0) { - req.log.debug('Don\'t fallback to DB because some filters are defined.'); - return util.setPaginationHeaders(req, res, - util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req)); + .then((result) => { + if (result.rows.length === 0) { + req.log.debug('No projects found in ES'); + + // if we have some filters and didn't get any data from ES + // we don't fallback to DB, because DB doesn't support all of the filters + // so we don't want DB to return unrelated data, ref issue #450 + if (_.intersection(_.keys(filters), SUPPORTED_FILTERS).length > 0) { + req.log.debug('Don\'t fallback to DB because some filters are defined.'); + return util.setPaginationHeaders(req, res, + util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req)); + } + + return retrieveProjectsFromDB(req, criteria, sort, req.query.fields) + .then(r => util.setPaginationHeaders(req, res, + util.postProcessInvites('$.rows[*].invites[?(@.email)]', r, req))); } - - return retrieveProjectsFromDB(req, criteria, sort, req.query.fields) - .then(r => util.setPaginationHeaders(req, res, - util.postProcessInvites('$.rows[*].invites[?(@.email)]', r, req))); - } - req.log.debug('Projects found in ES'); - // set header - return util.setPaginationHeaders(req, res, - util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req)); - }) + req.log.debug('Projects found in ES'); + // set header + return util.setPaginationHeaders(req, res, + util.postProcessInvites('$.rows[*].invites[?(@.email)]', result, req)); + }) .catch(err => next(err)); } diff --git a/src/routes/projects/list.spec.js b/src/routes/projects/list.spec.js index e454a181..c4ddb30d 100644 --- a/src/routes/projects/list.spec.js +++ b/src/routes/projects/list.spec.js @@ -407,22 +407,22 @@ describe('LIST Project', () => { it('should return the project when project that is in reviewed state in which the copilot is its member or has been invited', (done) => { request(server) - .get('/v5/projects') - .set({ - Authorization: `Bearer ${testUtil.jwts.copilot}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.lengthOf(2); - done(); - } - }); + .get('/v5/projects') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(2); + done(); + } + }); }); it('should return the project for administrator ', (done) => { @@ -1161,246 +1161,246 @@ describe('LIST Project', () => { describe('URL Query fields', () => { it('should not return "email" for project members when "fields" query param is not defined (to non-admin users)', (done) => { request(server) - .get('/v5/projects/') - .set({ - Authorization: `Bearer ${testUtil.jwts.member2}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.lengthOf(1); - resJson[0].members[0].should.not.have.property('email'); - done(); - } - }); + .get('/v5/projects/') + .set({ + Authorization: `Bearer ${testUtil.jwts.member2}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].members[0].should.not.have.property('email'); + done(); + } + }); }); it('should not return "email" for project members even if it\'s listed in "fields" query param (to non-admin users)', (done) => { request(server) - .get('/v5/projects/?fields=members.email,members.id') - .set({ - Authorization: `Bearer ${testUtil.jwts.member2}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.lengthOf(1); - resJson[0].members[0].should.not.have.property('email'); - done(); - } - }); + .get('/v5/projects/?fields=members.email,members.id') + .set({ + Authorization: `Bearer ${testUtil.jwts.member2}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].members[0].should.not.have.property('email'); + done(); + } + }); }); it('should not return "cancelReason" if it is not listed in "fields" query param ', (done) => { request(server) - .get('/v5/projects/?fields=description') - .set({ - Authorization: `Bearer ${testUtil.jwts.member2}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson.should.have.lengthOf(1); - resJson[0].should.have.property('description'); - resJson[0].should.not.have.property('cancelReason'); - resJson[0].description.should.be.eq('test project1 abc/d'); - done(); - } - }); + .get('/v5/projects/?fields=description') + .set({ + Authorization: `Bearer ${testUtil.jwts.member2}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(1); + resJson[0].should.have.property('description'); + resJson[0].should.not.have.property('cancelReason'); + resJson[0].description.should.be.eq('test project1 abc/d'); + done(); + } + }); }); it('should not return "email" for project members when it is not listed in "fields" query param (to admin users)', (done) => { request(server) - .get('/v5/projects/?fields=description,members.id') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - const member = _.find(project.members, m => m.id === 1); - member.should.not.have.property('email'); - done(); - } - }); + .get('/v5/projects/?fields=description,members.id') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + const member = _.find(project.members, m => m.id === 1); + member.should.not.have.property('email'); + done(); + } + }); }); it('should return "email" for project members if it\'s listed in "fields" query param (to admin users)', (done) => { request(server) - .get('/v5/projects/?fields=description,members.id,members.email') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - const member = _.find(project.members, m => m.id === 1); - member.should.have.property('email'); - member.email.should.be.eq('test@test.com'); - done(); - } - }); + .get('/v5/projects/?fields=description,members.id,members.email') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + const member = _.find(project.members, m => m.id === 1); + member.should.have.property('email'); + member.email.should.be.eq('test@test.com'); + done(); + } + }); }); it('should only return "id" field, when it\'s the only fields listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=id') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - resJson[0].should.have.property('id'); - _.keys(resJson[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=id') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson[0].should.have.property('id'); + _.keys(resJson[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "invites.userId" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=invites.userId') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - project.invites[0].should.have.property('userId'); - _.keys(project.invites[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=invites.userId') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + project.invites[0].should.have.property('userId'); + _.keys(project.invites[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "members.role" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=members.role') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - project.members[0].should.have.property('role'); - _.keys(project.members[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=members.role') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + project.members[0].should.have.property('role'); + _.keys(project.members[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "attachments.title" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=attachments.title') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - project.attachments[0].should.have.property('title'); - _.keys(project.attachments[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=attachments.title') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + project.attachments[0].should.have.property('title'); + _.keys(project.attachments[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "phases.name" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=phases.name') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - project.phases[0].should.have.property('name'); - _.keys(project.phases[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=phases.name') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + project.phases[0].should.have.property('name'); + _.keys(project.phases[0]).length.should.be.eq(1); + done(); + } + }); }); it('should only return "phases.products.name" field, when it\'s the only field listed in "fields" query param', (done) => { request(server) - .get('/v5/projects/?fields=phases.products.name') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err, res) => { - if (err) { - done(err); - } else { - const resJson = res.body; - should.exist(resJson); - const project = _.find(resJson, p => p.id === project1.id); - project.phases[0].products[0].should.have.property('name'); - _.keys(project.phases[0].products[0]).length.should.be.eq(1); - done(); - } - }); + .get('/v5/projects/?fields=phases.products.name') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + const project = _.find(resJson, p => p.id === project1.id); + project.phases[0].products[0].should.have.property('name'); + _.keys(project.phases[0].products[0]).length.should.be.eq(1); + done(); + } + }); }); it('should find a project by quoted keyword with a special symbol in the name', (done) => { diff --git a/src/routes/projects/update.js b/src/routes/projects/update.js index 00b76c32..83226638 100644 --- a/src/routes/projects/update.js +++ b/src/routes/projects/update.js @@ -74,7 +74,7 @@ const updateProjectValdiations = { users: Joi.array().items(Joi.number().positive()), groups: Joi.array().items(Joi.number().positive()), })).allow(null), - // cancel reason is mandatory when project status is cancelled + // cancel reason is mandatory when project status is cancelled cancelReason: Joi.when('status', { is: PROJECT_STATUS.CANCELLED, then: Joi.string().required(), @@ -160,7 +160,7 @@ module.exports = [ // handles request validations validate(updateProjectValdiations), permissions('project.edit'), - /** + /* * Validate project type to be existed. */ (req, res, next) => { @@ -179,7 +179,7 @@ module.exports = [ next(); } }, - /** + /* * POST projects/ * Create a project if the user has access */ @@ -208,7 +208,7 @@ module.exports = [ } if (!_prj.templateId) return Promise.resolve({ _prj }); return models.ProjectTemplate.getTemplate(_prj.templateId) - .then(template => Promise.resolve({ _prj, template })); + .then(template => Promise.resolve({ _prj, template })); }) .then(({ _prj, template }) => { project = _prj; diff --git a/src/routes/projects/update.spec.js b/src/routes/projects/update.spec.js index da91f27d..0ddd922b 100644 --- a/src/routes/projects/update.spec.js +++ b/src/routes/projects/update.spec.js @@ -660,17 +660,17 @@ describe('Project', () => { it('should return 400 when updating billingAccountId without "write:projects-billing-accounts" scope in M2M token', (done) => { - request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.m2m['write:projects']}`, - }) - .send({ - billingAccountId: 123, - }) - .expect('Content-Type', /json/) - .expect(400, done); - }); + request(server) + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.m2m['write:projects']}`, + }) + .send({ + billingAccountId: 123, + }) + .expect('Content-Type', /json/) + .expect(400, done); + }); it.skip('should return 200 and update bookmarks', (done) => { request(server) @@ -790,299 +790,299 @@ describe('Project', () => { it('should send correct BUS API messages when project status updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - status: PROJECT_STATUS.COMPLETED, - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - status: PROJECT_STATUS.COMPLETED, - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_COMPLETED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + status: PROJECT_STATUS.COMPLETED, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + status: PROJECT_STATUS.COMPLETED, + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_COMPLETED).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project details updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - details: { - info: 'something', - }, - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - details: { info: 'something' }, - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + details: { + info: 'something', + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + details: { info: 'something' }, + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project name updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - name: 'New project name', - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - name: 'New project name', - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ - projectId: project1.id, - projectName: 'New project name', - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + name: 'New project name', + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + name: 'New project name', + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ + projectId: project1.id, + projectName: 'New project name', + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project description updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - description: 'Updated description', - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - description: 'Updated description', - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + description: 'Updated description', + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + description: 'Updated description', + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_SPECIFICATION_MODIFIED).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project bookmarks updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - bookmarks: [{ - title: 'title1', - address: 'http://someurl.com', - }], - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - bookmarks: [{ title: 'title1', address: 'http://someurl.com' }], - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_LINK_CREATED).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ - projectId: project1.id, - projectName: project1.name, - projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + bookmarks: [{ + title: 'title1', + address: 'http://someurl.com', + }], + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + bookmarks: [{ title: 'title1', address: 'http://someurl.com' }], + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_LINK_CREATED).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project estimatedPrice updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - estimatedPrice: 123, - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(1); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - // FIXME https://github.com/sequelize/sequelize/issues/8019 - // estimatedPrice: 123, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + estimatedPrice: 123, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(1); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + // FIXME https://github.com/sequelize/sequelize/issues/8019 + // estimatedPrice: 123, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project actualPrice updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - actualPrice: 123, - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(1); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - // FIXME https://github.com/sequelize/sequelize/issues/8019 - // actualPrice: 123, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + actualPrice: 123, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(1); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + // FIXME https://github.com/sequelize/sequelize/issues/8019 + // actualPrice: 123, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when project terms are updated', (done) => { request(server) - .patch(`/v5/projects/${project1.id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - terms: [1, 2, 3], - }) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.equal(1); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ - resource: 'project', - id: project1.id, - terms: [1, 2, 3], - updatedBy: testUtil.userIds.admin, - })).should.be.true; + .patch(`/v5/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + terms: [1, 2, 3], + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.equal(1); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_UPDATED, sinon.match({ + resource: 'project', + id: project1.id, + terms: [1, 2, 3], + updatedBy: testUtil.userIds.admin, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); }); diff --git a/src/routes/scopeChangeRequests/create.js b/src/routes/scopeChangeRequests/create.js index e63caae9..d7848498 100644 --- a/src/routes/scopeChangeRequests/create.js +++ b/src/routes/scopeChangeRequests/create.js @@ -27,7 +27,7 @@ module.exports = [ const newScope = _.get(req, 'body.newScope'); const members = req.context.currentProjectMembers; const isCustomer = !_.isUndefined(_.find(members, - m => m.userId === req.authUser.userId && m.role === PROJECT_MEMBER_ROLE.CUSTOMER)); + m => m.userId === req.authUser.userId && m.role === PROJECT_MEMBER_ROLE.CUSTOMER)); const scopeChange = { oldScope, @@ -42,44 +42,44 @@ module.exports = [ where: { id: projectId }, }) - .then((project) => { - if (!project) { - const err = new Error(`Project with id ${projectId} not found`); - err.status = 404; - return Promise.reject(err); - } + .then((project) => { + if (!project) { + const err = new Error(`Project with id ${projectId} not found`); + err.status = 404; + return Promise.reject(err); + } - // If the project is not frozen yet, the changes can be saved directly into projects db. - // Scope change request workflow is not required. - const statusesForNonFrozenProjects = [PROJECT_STATUS.DRAFT, PROJECT_STATUS.IN_REVIEW]; - if (statusesForNonFrozenProjects.indexOf(project.status) > -1) { - const err = new Error( - `Cannot create a scope change request for projects with statuses: ${ - statusesForNonFrozenProjects.join(', ')}`); - err.status = 403; - return Promise.reject(err); - } + // If the project is not frozen yet, the changes can be saved directly into projects db. + // Scope change request workflow is not required. + const statusesForNonFrozenProjects = [PROJECT_STATUS.DRAFT, PROJECT_STATUS.IN_REVIEW]; + if (statusesForNonFrozenProjects.indexOf(project.status) > -1) { + const err = new Error( + `Cannot create a scope change request for projects with statuses: ${ + statusesForNonFrozenProjects.join(', ')}`); + err.status = 403; + return Promise.reject(err); + } - return models.ScopeChangeRequest.findPendingScopeChangeRequest(projectId); - }) + return models.ScopeChangeRequest.findPendingScopeChangeRequest(projectId); + }) - .then((pendingScopeChangeReq) => { - if (pendingScopeChangeReq) { - const err = new Error('Cannot create a new scope change request while there is a pending request'); - err.status = 403; - return Promise.reject(err); - } + .then((pendingScopeChangeReq) => { + if (pendingScopeChangeReq) { + const err = new Error('Cannot create a new scope change request while there is a pending request'); + err.status = 403; + return Promise.reject(err); + } - req.log.debug('creating scope change request'); - return models.ScopeChangeRequest.create(scopeChange); - }) + req.log.debug('creating scope change request'); + return models.ScopeChangeRequest.create(scopeChange); + }) - .then((_newScopeChange) => { - req.log.debug('Created scope change request'); - res.json(_newScopeChange); - return Promise.resolve(); - }) + .then((_newScopeChange) => { + req.log.debug('Created scope change request'); + res.json(_newScopeChange); + return Promise.resolve(); + }) - .catch(err => next(err)); + .catch(err => next(err)); }, ]; diff --git a/src/routes/scopeChangeRequests/update.js b/src/routes/scopeChangeRequests/update.js index 1f1e8ea5..6111e248 100644 --- a/src/routes/scopeChangeRequests/update.js +++ b/src/routes/scopeChangeRequests/update.js @@ -4,11 +4,11 @@ import validate from 'express-validation'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import { - SCOPE_CHANGE_REQ_STATUS, - PROJECT_MEMBER_ROLE, - USER_ROLE, - PROJECT_MEMBER_MANAGER_ROLES, - EVENT, + SCOPE_CHANGE_REQ_STATUS, + PROJECT_MEMBER_ROLE, + USER_ROLE, + PROJECT_MEMBER_MANAGER_ROLES, + EVENT, } from '../../constants'; import models from '../../models'; @@ -84,44 +84,44 @@ module.exports = [ req.log.debug('finding scope change', requestId); return models.ScopeChangeRequest.findScopeChangeRequest(projectId, { requestId }) - .then((scopeChangeReq) => { + .then((scopeChangeReq) => { // req.log.debug(scopeChangeReq); - if (!scopeChangeReq) { - const err = new Error('Scope change request does not exist'); - err.status = 404; - return next(err); - } - const statusesForCustomers = [SCOPE_CHANGE_REQ_STATUS.APPROVED, SCOPE_CHANGE_REQ_STATUS.REJECTED]; - if (statusesForCustomers.indexOf(updatedProps.status) > -1 && !isCustomer && !isAdmin) { - const err = new Error('Only customer can approve the request'); - err.status = 401; - return next(err); - } - const statusesForManagers = [SCOPE_CHANGE_REQ_STATUS.ACTIVATED]; - if (statusesForManagers.indexOf(updatedProps.status) > -1 && !isManager && !isAdmin) { - const err = new Error('Only managers can activate the request'); - err.status = 401; - return next(err); - } - const statusesForSelf = [SCOPE_CHANGE_REQ_STATUS.CANCELED]; - const isSelf = scopeChangeReq.createdBy === req.authUser.userId; - if (statusesForSelf.indexOf(updatedProps.status) > -1 && !isSelf && !isAdmin) { - const err = new Error('One can cancel only own requests'); - err.status = 401; - return next(err); - } + if (!scopeChangeReq) { + const err = new Error('Scope change request does not exist'); + err.status = 404; + return next(err); + } + const statusesForCustomers = [SCOPE_CHANGE_REQ_STATUS.APPROVED, SCOPE_CHANGE_REQ_STATUS.REJECTED]; + if (statusesForCustomers.indexOf(updatedProps.status) > -1 && !isCustomer && !isAdmin) { + const err = new Error('Only customer can approve the request'); + err.status = 401; + return next(err); + } + const statusesForManagers = [SCOPE_CHANGE_REQ_STATUS.ACTIVATED]; + if (statusesForManagers.indexOf(updatedProps.status) > -1 && !isManager && !isAdmin) { + const err = new Error('Only managers can activate the request'); + err.status = 401; + return next(err); + } + const statusesForSelf = [SCOPE_CHANGE_REQ_STATUS.CANCELED]; + const isSelf = scopeChangeReq.createdBy === req.authUser.userId; + if (statusesForSelf.indexOf(updatedProps.status) > -1 && !isSelf && !isAdmin) { + const err = new Error('One can cancel only own requests'); + err.status = 401; + return next(err); + } - return ( - updatedProps.status === SCOPE_CHANGE_REQ_STATUS.ACTIVATED - ? updateProjectDetails(req, scopeChangeReq.newScope, projectId) - : Promise.resolve() - ) - .then(() => scopeChangeReq.update(updatedProps)) - .then((_updatedReq) => { - res.json(_updatedReq); - return Promise.resolve(); - }); - }) - .catch(err => next(err)); + return ( + updatedProps.status === SCOPE_CHANGE_REQ_STATUS.ACTIVATED + ? updateProjectDetails(req, scopeChangeReq.newScope, projectId) + : Promise.resolve() + ) + .then(() => scopeChangeReq.update(updatedProps)) + .then((_updatedReq) => { + res.json(_updatedReq); + return Promise.resolve(); + }); + }) + .catch(err => next(err)); }, ]; diff --git a/src/routes/timelines/create.js b/src/routes/timelines/create.js index cb2e09d0..bc523dcc 100644 --- a/src/routes/timelines/create.js +++ b/src/routes/timelines/create.js @@ -96,10 +96,10 @@ module.exports = [ return milestone; }); return models.Milestone.bulkCreate(milestones, { returning: true }) - .then((createdMilestones) => { - req.log.debug('Milestones created for timeline with template id %d', templateId); - result.milestones = _.map(createdMilestones, cm => _.omit(cm.toJSON(), 'deletedAt', 'deletedBy')); - }); + .then((createdMilestones) => { + req.log.debug('Milestones created for timeline with template id %d', templateId); + result.milestones = _.map(createdMilestones, cm => _.omit(cm.toJSON(), 'deletedAt', 'deletedBy')); + }); } // no milestone template found for the template req.log.debug('no milestone template found for the template id %d', templateId); @@ -110,31 +110,31 @@ module.exports = [ }) .catch(next); }) - .then(() => { + .then(() => { // Send event to bus - req.log.debug('Sending event to RabbitMQ bus for timeline %d', result.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.TIMELINE_ADDED, - _.assign({ projectId: req.params.projectId }, result), - { correlationId: req.id }, - ); + req.log.debug('Sending event to RabbitMQ bus for timeline %d', result.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.TIMELINE_ADDED, + _.assign({ projectId: req.params.projectId }, result), + { correlationId: req.id }, + ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.TIMELINE_ADDED, - RESOURCES.TIMELINE, - result); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.TIMELINE_ADDED, + RESOURCES.TIMELINE, + result); - // emit the event for milestones - _.map(result.milestones, milestone => util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.MILESTONE_ADDED, - RESOURCES.MILESTONE, - milestone)); + // emit the event for milestones + _.map(result.milestones, milestone => util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.MILESTONE_ADDED, + RESOURCES.MILESTONE, + milestone)); - // Write to the response - res.status(201).json(result); - return Promise.resolve(); - }) - .catch(next); + // Write to the response + res.status(201).json(result); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/timelines/delete.js b/src/routes/timelines/delete.js index 03e5e7d7..7a8af7eb 100644 --- a/src/routes/timelines/delete.js +++ b/src/routes/timelines/delete.js @@ -45,31 +45,31 @@ module.exports = [ limit: itemsDeleted, })), ) - .then((milestones) => { + .then((milestones) => { // Send event to bus - req.log.debug('Sending event to RabbitMQ bus for timeline %d', deleted.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.TIMELINE_REMOVED, - deleted, - { correlationId: req.id }, - ); + req.log.debug('Sending event to RabbitMQ bus for timeline %d', deleted.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.TIMELINE_REMOVED, + deleted, + { correlationId: req.id }, + ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.TIMELINE_REMOVED, - RESOURCES.TIMELINE, - { id: req.params.timelineId }); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.TIMELINE_REMOVED, + RESOURCES.TIMELINE, + { id: req.params.timelineId }); - // emit the event for milestones - _.map(milestones, milestone => util.sendResourceToKafkaBus(req, - EVENT.ROUTING_KEY.MILESTONE_REMOVED, - RESOURCES.MILESTONE, - milestone.toJSON())); + // emit the event for milestones + _.map(milestones, milestone => util.sendResourceToKafkaBus(req, + EVENT.ROUTING_KEY.MILESTONE_REMOVED, + RESOURCES.MILESTONE, + milestone.toJSON())); - // Write to response - res.status(204).end(); - return Promise.resolve(); - }) - .catch(next); + // Write to response + res.status(204).end(); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/timelines/delete.spec.js b/src/routes/timelines/delete.spec.js index e082578c..d75b47ad 100644 --- a/src/routes/timelines/delete.spec.js +++ b/src/routes/timelines/delete.spec.js @@ -14,27 +14,27 @@ const should = chai.should(); // eslint-disable-line no-unused-vars const expectAfterDelete = (id, err, next) => { if (err) throw err; setTimeout(() => - models.Timeline.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.Timeline.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/timelines/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/timelines/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE timeline', () => { diff --git a/src/routes/timelines/get.js b/src/routes/timelines/get.js index f5145ffa..3d62909d 100644 --- a/src/routes/timelines/get.js +++ b/src/routes/timelines/get.js @@ -53,16 +53,16 @@ module.exports = [ type: ES_TIMELINE_TYPE, id: req.params.timelineId, }) - .then((doc) => { - req.log.debug('timeline found in ES'); - return res.json(doc._source); // eslint-disable-line no-underscore-dangle - }) - .catch((err) => { - if (err.status === 404) { - req.log.debug('No timeline found in ES'); - return loadMilestones(req.timeline).then(timeline => res.json(timeline)); - } - return next(err); - }); + .then((doc) => { + req.log.debug('timeline found in ES'); + return res.json(doc._source); // eslint-disable-line no-underscore-dangle + }) + .catch((err) => { + if (err.status === 404) { + req.log.debug('No timeline found in ES'); + return loadMilestones(req.timeline).then(timeline => res.json(timeline)); + } + return next(err); + }); }, ]; diff --git a/src/routes/timelines/get.spec.js b/src/routes/timelines/get.spec.js index 82ac3b50..001c7324 100644 --- a/src/routes/timelines/get.spec.js +++ b/src/routes/timelines/get.spec.js @@ -186,7 +186,7 @@ describe('GET timeline', () => { .then(createdTimelines => ( // create milestones after timelines models.Milestone.bulkCreate(milestones)) - .then(createdMilestones => [createdTimelines, createdMilestones]), + .then(createdMilestones => [createdTimelines, createdMilestones]), ), ).then(([createdTimelines, createdMilestones]) => // Index to ES diff --git a/src/routes/timelines/list.js b/src/routes/timelines/list.js index d9ce06f4..33f06a90 100644 --- a/src/routes/timelines/list.js +++ b/src/routes/timelines/list.js @@ -45,22 +45,22 @@ function retrieveTimelines(esTerms) { */ function retrieveTimelinesFromDB(req, filters) { return models.Timeline.search(filters, req.log) - .then((timelines) => { - const timelineIds = _.map(timelines, 'id'); + .then((timelines) => { + const timelineIds = _.map(timelines, 'id'); - // retrieve milestones - return models.Milestone.findAll({ - attributes: MILESTONE_ATTRIBUTES, - where: { timelineId: { $in: timelineIds } }, - raw: true, - }) - .then((values) => { - _.forEach(timelines, (t) => { - t.milestones = _.filter(values, m => m.timelineId === t.id); // eslint-disable-line no-param-reassign - }); - return timelines; + // retrieve milestones + return models.Milestone.findAll({ + attributes: MILESTONE_ATTRIBUTES, + where: { timelineId: { $in: timelineIds } }, + raw: true, + }) + .then((values) => { + _.forEach(timelines, (t) => { + t.milestones = _.filter(values, m => m.timelineId === t.id); // eslint-disable-line no-param-reassign + }); + return timelines; + }); }); - }); } const permissions = tcMiddleware.permissions; diff --git a/src/routes/timelines/list.spec.js b/src/routes/timelines/list.spec.js index ffed6389..04a9640e 100644 --- a/src/routes/timelines/list.spec.js +++ b/src/routes/timelines/list.spec.js @@ -149,43 +149,43 @@ describe('LIST timelines', () => { }, ]).then(() => // Create phase - models.ProjectPhase.bulkCreate([ - { - projectId: 1, - name: 'test project phase 1', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json 2', - }, - createdBy: 1, - updatedBy: 1, - }, - { - projectId: 2, - name: 'test project phase 2', - status: 'active', - startDate: '2018-05-16T00:00:00Z', - endDate: '2018-05-16T12:00:00Z', - budget: 21.0, - progress: 1.234567, - details: { - message: 'This can be any json 2', - }, - createdBy: 2, - updatedBy: 2, - }, - ])) + models.ProjectPhase.bulkCreate([ + { + projectId: 1, + name: 'test project phase 1', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json 2', + }, + createdBy: 1, + updatedBy: 1, + }, + { + projectId: 2, + name: 'test project phase 2', + status: 'active', + startDate: '2018-05-16T00:00:00Z', + endDate: '2018-05-16T12:00:00Z', + budget: 21.0, + progress: 1.234567, + details: { + message: 'This can be any json 2', + }, + createdBy: 2, + updatedBy: 2, + }, + ])) .then(() => // Create timelines models.Timeline.bulkCreate(timelines, { returning: true }) .then(createdTimelines => ( // create milestones after timelines models.Milestone.bulkCreate(milestones)) - .then(createdMilestones => [createdTimelines, createdMilestones]), + .then(createdMilestones => [createdTimelines, createdMilestones]), ), ).then(([createdTimelines, createdMilestones]) => // Index to ES diff --git a/src/routes/workItems/create.js b/src/routes/workItems/create.js index f53ef1b4..0852a26d 100644 --- a/src/routes/workItems/create.js +++ b/src/routes/workItems/create.js @@ -76,64 +76,64 @@ module.exports = [ raw: true, }); }) - .then((existingProject) => { + .then((existingProject) => { // make sure project exists - if (!existingProject) { - const err = new Error(`project not found for project id ${projectId}`); - err.status = 404; - throw err; - } + if (!existingProject) { + const err = new Error(`project not found for project id ${projectId}`); + err.status = 404; + throw err; + } - _.assign(data, { - phaseId, - projectId, - directProjectId: existingProject.directProjectId, - billingAccountId: existingProject.billingAccountId, - }); - - return models.PhaseProduct.count({ - where: { - projectId, + _.assign(data, { phaseId, - deletedAt: { $eq: null }, - }, - raw: true, - }); - }) - .then((productCount) => { + projectId, + directProjectId: existingProject.directProjectId, + billingAccountId: existingProject.billingAccountId, + }); + + return models.PhaseProduct.count({ + where: { + projectId, + phaseId, + deletedAt: { $eq: null }, + }, + raw: true, + }); + }) + .then((productCount) => { // make sure number of products of per phase <= max value - if (productCount >= 100) { - const err = new Error('the number of products per phase cannot exceed ' + + if (productCount >= 100) { + const err = new Error('the number of products per phase cannot exceed ' + `${100}`); - err.status = 400; - throw err; - } - return models.PhaseProduct.create(data) - .then((_newPhaseProduct) => { - newPhaseProduct = _.cloneDeep(_newPhaseProduct); - req.log.debug('new work created (id# %d, name: %s)', - newPhaseProduct.id, newPhaseProduct.name); - newPhaseProduct = newPhaseProduct.get({ plain: true }); - newPhaseProduct = _.omit(newPhaseProduct, ['deletedAt', 'utm']); - }); - })) - .then(() => { + err.status = 400; + throw err; + } + return models.PhaseProduct.create(data) + .then((_newPhaseProduct) => { + newPhaseProduct = _.cloneDeep(_newPhaseProduct); + req.log.debug('new work created (id# %d, name: %s)', + newPhaseProduct.id, newPhaseProduct.name); + newPhaseProduct = newPhaseProduct.get({ plain: true }); + newPhaseProduct = _.omit(newPhaseProduct, ['deletedAt', 'utm']); + }); + })) + .then(() => { // Send events to buses - req.log.debug('Sending event to RabbitMQ bus for phase product %d', newPhaseProduct.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, - newPhaseProduct, - { correlationId: req.id }, - ); - req.log.debug('Sending event to Kafka bus for phase product %d', newPhaseProduct.id); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, - RESOURCES.PHASE_PRODUCT, - newPhaseProduct); + req.log.debug('Sending event to RabbitMQ bus for phase product %d', newPhaseProduct.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, + newPhaseProduct, + { correlationId: req.id }, + ); + req.log.debug('Sending event to Kafka bus for phase product %d', newPhaseProduct.id); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, + RESOURCES.PHASE_PRODUCT, + newPhaseProduct); - res.status(201).json(newPhaseProduct); - }) - .catch((err) => { next(err); }); + res.status(201).json(newPhaseProduct); + }) + .catch((err) => { next(err); }); }, ]; diff --git a/src/routes/workItems/create.spec.js b/src/routes/workItems/create.spec.js index 0e7e3482..8e447acc 100644 --- a/src/routes/workItems/create.spec.js +++ b/src/routes/workItems/create.spec.js @@ -62,92 +62,92 @@ describe('CREATE Work Item', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'workItem.create', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'workItem.create', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, + projectTemplateId: template.id, details: {}, createdBy: 1, updatedBy: 1, lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, + .then(() => { + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, createdBy: 1, updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }).then(() => { - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', projectId, - role: 'copilot', - isPrimary: false, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => done()); + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, + createdBy: 1, + updatedBy: 1, + projectId, + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }).then(() => { + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); + }); + }); + }); }); - }); }); - }); }); - }); }); }); @@ -316,28 +316,28 @@ describe('CREATE Work Item', () => { it('should send correct BUS API messages when work item created', (done) => { request(server) - .post(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}/workitems`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .post(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}/workitems`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ - resource: RESOURCES.PHASE_PRODUCT, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ + resource: RESOURCES.PHASE_PRODUCT, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); }); diff --git a/src/routes/workItems/delete.js b/src/routes/workItems/delete.js index bab03805..6c561fd4 100644 --- a/src/routes/workItems/delete.js +++ b/src/routes/workItems/delete.js @@ -32,49 +32,49 @@ module.exports = [ const productId = _.parseInt(req.params.id); models.sequelize.transaction(() => - models.ProjectPhase.findOne({ - where: { - id: phaseId, - }, - include: [{ - model: models.WorkStream, + models.ProjectPhase.findOne({ where: { - id: workStreamId, - projectId, + id: phaseId, }, - }, - ], - }) - .then((existing) => { - if (!existing) { - // handle 404 - const err = new Error('No active work item found for project id ' + + include: [{ + model: models.WorkStream, + where: { + id: workStreamId, + projectId, + }, + }, + ], + }) + .then((existing) => { + if (!existing) { + // handle 404 + const err = new Error('No active work item found for project id ' + `${projectId}, phase id ${phaseId} and work stream id ${workStreamId}`); - err.status = 404; - return Promise.reject(err); - } + err.status = 404; + return Promise.reject(err); + } - // soft delete the record - return models.PhaseProduct.findOne({ - where: { - id: productId, - projectId, - phaseId, - deletedAt: { $eq: null }, - }, - }); - }) - .then((existing) => { - if (!existing) { + // soft delete the record + return models.PhaseProduct.findOne({ + where: { + id: productId, + projectId, + phaseId, + deletedAt: { $eq: null }, + }, + }); + }) + .then((existing) => { + if (!existing) { // handle 404 - const err = new Error('No active work item found for project id ' + + const err = new Error('No active work item found for project id ' + `${projectId}, phase id ${phaseId} and product id ${productId}`); - err.status = 404; - return Promise.reject(err); - } - return existing.update({ deletedBy: req.authUser.userId }); - }) - .then(entity => entity.destroy())) + err.status = 404; + return Promise.reject(err); + } + return existing.update({ deletedBy: req.authUser.userId }); + }) + .then(entity => entity.destroy())) .then((deleted) => { req.log.debug('deleted work item', JSON.stringify(deleted, null, 2)); diff --git a/src/routes/workItems/delete.spec.js b/src/routes/workItems/delete.spec.js index f9e98731..6c93a78c 100644 --- a/src/routes/workItems/delete.spec.js +++ b/src/routes/workItems/delete.spec.js @@ -19,29 +19,29 @@ chai.should(); const expectAfterDelete = (projectId, workStreamId, phaseId, id, err, next) => { if (err) throw err; setTimeout(() => - models.PhaseProduct.findOne({ - where: { - id, - projectId, - phaseId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.PhaseProduct.findOne({ + where: { + id, + projectId, + phaseId, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${phaseId}/workitems/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${phaseId}/workitems/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; const body = { name: 'test phase product', @@ -93,97 +93,97 @@ describe('DELETE Work Item', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'workItem.delete', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'workItem.delete', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, + projectTemplateId: template.id, details: {}, createdBy: 1, updatedBy: 1, lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, + .then(() => { + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, createdBy: 1, updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }) - .then(() => { - _.assign(body, { phaseId: workId, projectId }); - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => done()); + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }) + .then(() => { + _.assign(body, { phaseId: workId, projectId }); + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); + }); + }); + }); }); }); - }); }); - }); }); - }); }); }); @@ -265,26 +265,26 @@ describe('DELETE Work Item', () => { it('should send correct BUS API messages when work item removed', (done) => { request(server) - .delete(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}/workitems/${productId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .delete(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}/workitems/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ - resource: RESOURCES.PHASE_PRODUCT, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ + resource: RESOURCES.PHASE_PRODUCT, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); }); diff --git a/src/routes/workItems/get.js b/src/routes/workItems/get.js index 44d20716..d2f4f8fe 100644 --- a/src/routes/workItems/get.js +++ b/src/routes/workItems/get.js @@ -42,33 +42,33 @@ module.exports = [ }, ], }) - .then((existing) => { - if (!existing) { + .then((existing) => { + if (!existing) { // handle 404 - const err = new Error('No active work item found for project id ' + + const err = new Error('No active work item found for project id ' + `${projectId}, phase id ${phaseId} and work stream id ${workStreamId}`); - err.status = 404; - return Promise.reject(err); - } + err.status = 404; + return Promise.reject(err); + } - return models.PhaseProduct.findOne({ - where: { - id: productId, - projectId, - phaseId, - deletedAt: { $eq: null }, - }, - }); - }).then((product) => { - if (!product) { + return models.PhaseProduct.findOne({ + where: { + id: productId, + projectId, + phaseId, + deletedAt: { $eq: null }, + }, + }); + }).then((product) => { + if (!product) { // handle 404 - const err = new Error('phase product not found for project id ' + + const err = new Error('phase product not found for project id ' + `${projectId}, phase id ${phaseId} and product id ${productId}`); - err.status = 404; - throw err; - } else { - res.json(product); - } - }).catch(err => next(err)); + err.status = 404; + throw err; + } else { + res.json(product); + } + }).catch(err => next(err)); }, ]; diff --git a/src/routes/workItems/get.spec.js b/src/routes/workItems/get.spec.js index b3acd08c..121e4747 100644 --- a/src/routes/workItems/get.spec.js +++ b/src/routes/workItems/get.spec.js @@ -74,66 +74,66 @@ describe('GET Work Item', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]) - .then(() => { - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', + .then((project) => { + projectId = project.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }) - .then(() => { - _.assign(body, { phaseId: workId, projectId }); - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - done(); + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]) + .then(() => { + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, + createdBy: 1, + updatedBy: 1, + projectId, + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }) + .then(() => { + _.assign(body, { phaseId: workId, projectId }); + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + done(); + }); + }); }); }); }); - }); }); - }); }); }); }); diff --git a/src/routes/workItems/list.js b/src/routes/workItems/list.js index b9dd656d..ad17ffd6 100644 --- a/src/routes/workItems/list.js +++ b/src/routes/workItems/list.js @@ -41,23 +41,23 @@ module.exports = [ }, ], }) - .then((existing) => { - if (!existing) { + .then((existing) => { + if (!existing) { // handle 404 - const err = new Error('No active phase product found for project id ' + + const err = new Error('No active phase product found for project id ' + `${projectId}, work stream id ${workStreamId} and phase id ${phaseId}`); - err.status = 404; - throw err; - } + err.status = 404; + throw err; + } - return models.PhaseProduct.findAll({ - where: { - phaseId, - projectId, - }, - }); - }) - .then(products => res.json(products)) - .catch(err => next(err)); + return models.PhaseProduct.findAll({ + where: { + phaseId, + projectId, + }, + }); + }) + .then(products => res.json(products)) + .catch(err => next(err)); }, ]; diff --git a/src/routes/workItems/list.spec.js b/src/routes/workItems/list.spec.js index bb2cd9fb..2147f6e1 100644 --- a/src/routes/workItems/list.spec.js +++ b/src/routes/workItems/list.spec.js @@ -73,63 +73,63 @@ describe('LIST Work Items', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]) - .then(() => { - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', + .then((project) => { + projectId = project.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }) - .then(() => { - _.assign(body, { phaseId: workId, projectId }); - models.PhaseProduct.create(body).then(() => done()); + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]) + .then(() => { + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, + createdBy: 1, + updatedBy: 1, + projectId, + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }) + .then(() => { + _.assign(body, { phaseId: workId, projectId }); + models.PhaseProduct.create(body).then(() => done()); + }); + }); }); }); - }); }); - }); }); }); }); diff --git a/src/routes/workItems/update.js b/src/routes/workItems/update.js index d22e00bd..77daab17 100644 --- a/src/routes/workItems/update.js +++ b/src/routes/workItems/update.js @@ -62,58 +62,58 @@ module.exports = [ }, ], }) - .then((existingWork) => { - if (!existingWork) { + .then((existingWork) => { + if (!existingWork) { // handle 404 - const err = new Error('No active work item found for project id ' + + const err = new Error('No active work item found for project id ' + `${projectId}, phase id ${phaseId} and work stream id ${workStreamId}`); - err.status = 404; - return Promise.reject(err); - } + err.status = 404; + return Promise.reject(err); + } - return models.PhaseProduct.findOne({ - where: { - id: productId, - projectId, - phaseId, - deletedAt: { $eq: null }, - }, - }); - }) - .then((existing) => { - if (!existing) { + return models.PhaseProduct.findOne({ + where: { + id: productId, + projectId, + phaseId, + deletedAt: { $eq: null }, + }, + }); + }) + .then((existing) => { + if (!existing) { // handle 404 - const err = new Error('No active phase product found for project id ' + + const err = new Error('No active phase product found for project id ' + `${projectId}, phase id ${phaseId} and product id ${productId}`); - err.status = 404; - throw err; - } + err.status = 404; + throw err; + } - previousValue = _.clone(existing.get({ plain: true })); - _.extend(existing, updatedProps); - return existing.save().catch(next); - })) - .then((updated) => { - req.log.debug('updated work item', JSON.stringify(updated, null, 2)); + previousValue = _.clone(existing.get({ plain: true })); + _.extend(existing, updatedProps); + return existing.save().catch(next); + })) + .then((updated) => { + req.log.debug('updated work item', JSON.stringify(updated, null, 2)); - const updatedValue = updated.get({ plain: true }); + const updatedValue = updated.get({ plain: true }); - // emit original and updated project phase information - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, - { original: previousValue, updated: updatedValue }, - { correlationId: req.id }, - ); - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, - RESOURCES.PHASE_PRODUCT, - updatedValue, - previousValue, - ROUTES.WORK_ITEMS.UPDATE, - ); + // emit original and updated project phase information + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, + { original: previousValue, updated: updatedValue }, + { correlationId: req.id }, + ); + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_UPDATED, + RESOURCES.PHASE_PRODUCT, + updatedValue, + previousValue, + ROUTES.WORK_ITEMS.UPDATE, + ); - res.json(updated); - }).catch(err => next(err)); + res.json(updated); + }).catch(err => next(err)); }, ]; diff --git a/src/routes/workItems/update.spec.js b/src/routes/workItems/update.spec.js index 311e7160..628ffd1d 100644 --- a/src/routes/workItems/update.spec.js +++ b/src/routes/workItems/update.spec.js @@ -74,97 +74,97 @@ describe('UPDATE Work Item', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'workItem.edit', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'workItem.edit', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, + projectTemplateId: template.id, details: {}, createdBy: 1, updatedBy: 1, lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, + .then(() => { + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, createdBy: 1, updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }) - .then(() => { - _.assign(body, { phaseId: workId, projectId }); - models.PhaseProduct.create(body).then((product) => { - productId = product.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, createdBy: 1, updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]).then(() => done()); + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }) + .then(() => { + _.assign(body, { phaseId: workId, projectId }); + models.PhaseProduct.create(body).then((product) => { + productId = product.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); + }); + }); + }); }); }); - }); }); - }); }); - }); }); }); diff --git a/src/routes/workManagementPermissions/create.spec.js b/src/routes/workManagementPermissions/create.spec.js index 8c8eb190..c6554f87 100644 --- a/src/routes/workManagementPermissions/create.spec.js +++ b/src/routes/workManagementPermissions/create.spec.js @@ -43,10 +43,10 @@ describe('CREATE work management permission', () => { createdBy: 1, updatedBy: 2, }) - .then((t) => { - templateId = t.id; - body.projectTemplateId = templateId; - }).then(() => done()); + .then((t) => { + templateId = t.id; + body.projectTemplateId = templateId; + }).then(() => done()); }); }); diff --git a/src/routes/workManagementPermissions/delete.js b/src/routes/workManagementPermissions/delete.js index 1228918a..3080e9bb 100644 --- a/src/routes/workManagementPermissions/delete.js +++ b/src/routes/workManagementPermissions/delete.js @@ -18,7 +18,7 @@ module.exports = [ validate(schema), permissions('workManagementPermission.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.WorkManagementPermission.findByPk(req.params.id) .then((entity) => { if (!entity) { @@ -30,8 +30,8 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then(() => { - res.status(204).end(); - }) - .catch(next), + .then(() => { + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/workManagementPermissions/delete.spec.js b/src/routes/workManagementPermissions/delete.spec.js index 9a24dcff..1a72f944 100644 --- a/src/routes/workManagementPermissions/delete.spec.js +++ b/src/routes/workManagementPermissions/delete.spec.js @@ -11,28 +11,28 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (permissionId, err, next) => { if (err) throw err; setTimeout(() => - models.WorkManagementPermission.findOne({ - where: { - id: permissionId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); - - request(server) - .get(`/v5/projects/metadata/workManagementPermission/${permissionId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404) - .end(next); - } - }), 500); + models.WorkManagementPermission.findOne({ + where: { + id: permissionId, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); + + request(server) + .get(`/v5/projects/metadata/workManagementPermission/${permissionId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404) + .end(next); + } + }), 500); }; describe('DELETE work management permission', () => { @@ -82,49 +82,49 @@ describe('DELETE work management permission', () => { createdBy: 1, updatedBy: 2, }) - .then((t) => { - permission = _.assign({}, permission, { projectTemplateId: t.id }); - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: t.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then((project) => { - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId: project.id, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId: project.id, - role: 'customer', - isPrimary: true, + .then((t) => { + permission = _.assign({}, permission, { projectTemplateId: t.id }); + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: t.id, + details: {}, createdBy: 1, updatedBy: 1, - }]).then(() => { - models.WorkManagementPermission.create(permission) - .then((p) => { - permissionId = p.id; - }) - .then(() => done()); - }); + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId: project.id, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId: project.id, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + models.WorkManagementPermission.create(permission) + .then((p) => { + permissionId = p.id; + }) + .then(() => done()); + }); + }); }); - }); }); }); diff --git a/src/routes/workManagementPermissions/get.spec.js b/src/routes/workManagementPermissions/get.spec.js index 29457681..8794ba9a 100644 --- a/src/routes/workManagementPermissions/get.spec.js +++ b/src/routes/workManagementPermissions/get.spec.js @@ -43,14 +43,14 @@ describe('GET work management permission', () => { createdBy: 1, updatedBy: 2, }) - .then((t) => { - permission = _.assign({}, permission, { projectTemplateId: t.id }); - models.WorkManagementPermission.create(permission) - .then((p) => { - permissionId = p.id; - }) - .then(() => done()); - }); + .then((t) => { + permission = _.assign({}, permission, { projectTemplateId: t.id }); + models.WorkManagementPermission.create(permission) + .then((p) => { + permissionId = p.id; + }) + .then(() => done()); + }); }); }); diff --git a/src/routes/workManagementPermissions/list.js b/src/routes/workManagementPermissions/list.js index cef27bd8..c6720661 100644 --- a/src/routes/workManagementPermissions/list.js +++ b/src/routes/workManagementPermissions/list.js @@ -35,9 +35,9 @@ module.exports = [ attributes: { exclude: ['deletedAt', 'deletedBy'] }, raw: true, }) - .then((result) => { - res.json(result); - }) - .catch(next); + .then((result) => { + res.json(result); + }) + .catch(next); }, ]; diff --git a/src/routes/workManagementPermissions/list.spec.js b/src/routes/workManagementPermissions/list.spec.js index 0a9728ad..024d722c 100644 --- a/src/routes/workManagementPermissions/list.spec.js +++ b/src/routes/workManagementPermissions/list.spec.js @@ -96,13 +96,13 @@ describe('LIST work management permissions', () => { testUtil.clearDb() .then(() => { models.ProjectTemplate.bulkCreate(templates, { returning: true }) - .then((t) => { - templateIds = _.map(t, template => template.id); - const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateIds[0] })); - newPermissions.push(_.assign({}, permissions[0], { projectTemplateId: templateIds[1] })); - models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true }) - .then(() => done()); - }); + .then((t) => { + templateIds = _.map(t, template => template.id); + const newPermissions = _.map(permissions, p => _.assign({}, p, { projectTemplateId: templateIds[0] })); + newPermissions.push(_.assign({}, permissions[0], { projectTemplateId: templateIds[1] })); + models.WorkManagementPermission.bulkCreate(newPermissions, { returning: true }) + .then(() => done()); + }); }); }); diff --git a/src/routes/workManagementPermissions/update.js b/src/routes/workManagementPermissions/update.js index 7e18851b..28839424 100644 --- a/src/routes/workManagementPermissions/update.js +++ b/src/routes/workManagementPermissions/update.js @@ -44,33 +44,33 @@ module.exports = [ }, attributes: { exclude: ['deletedAt', 'deletedBy'] }, }) - .then((permission) => { + .then((permission) => { // Not found - if (!permission) { - const apiErr = new Error(`Work Management Permission not found for id ${req.params.id}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } + if (!permission) { + const apiErr = new Error(`Work Management Permission not found for id ${req.params.id}`); + apiErr.status = 404; + return Promise.reject(apiErr); + } - permissionToUpdate = permission; - return models.WorkManagementPermission.findOne({ - where: { - policy: entityToUpdate.policy, - projectTemplateId: entityToUpdate.projectTemplateId, - id: { $ne: req.params.id }, - }, - paranoid: false, - }); - }) - .then((existing) => { - if (existing) { - const apiErr = new Error(`Work Management Permission already exists (may be deleted) for policy "${entityToUpdate.policy}" and project template id ${entityToUpdate.projectTemplateId}`); - apiErr.status = 400; - return Promise.reject(apiErr); - } + permissionToUpdate = permission; + return models.WorkManagementPermission.findOne({ + where: { + policy: entityToUpdate.policy, + projectTemplateId: entityToUpdate.projectTemplateId, + id: { $ne: req.params.id }, + }, + paranoid: false, + }); + }) + .then((existing) => { + if (existing) { + const apiErr = new Error(`Work Management Permission already exists (may be deleted) for policy "${entityToUpdate.policy}" and project template id ${entityToUpdate.projectTemplateId}`); + apiErr.status = 400; + return Promise.reject(apiErr); + } - return permissionToUpdate.update(entityToUpdate); - }), + return permissionToUpdate.update(entityToUpdate); + }), ) .then((updated) => { res.json(updated); diff --git a/src/routes/workManagementPermissions/update.spec.js b/src/routes/workManagementPermissions/update.spec.js index cb162481..6c1b53c8 100644 --- a/src/routes/workManagementPermissions/update.spec.js +++ b/src/routes/workManagementPermissions/update.spec.js @@ -44,15 +44,15 @@ describe('UPDATE work management permission', () => { createdBy: 1, updatedBy: 2, }) - .then((t) => { - templateId = t.id; - permission = _.assign({}, permission, { projectTemplateId: templateId }); - models.WorkManagementPermission.create(permission) - .then((p) => { - permissionId = p.id; - }) - .then(() => done()); - }); + .then((t) => { + templateId = t.id; + permission = _.assign({}, permission, { projectTemplateId: templateId }); + models.WorkManagementPermission.create(permission) + .then((p) => { + permissionId = p.id; + }) + .then(() => done()); + }); }); }); diff --git a/src/routes/workStreams/create.spec.js b/src/routes/workStreams/create.spec.js index 4a66cdb8..33640b41 100644 --- a/src/routes/workStreams/create.spec.js +++ b/src/routes/workStreams/create.spec.js @@ -85,9 +85,9 @@ describe('CREATE work stream', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - }) + .then((project) => { + projectId = project.id; + }) .then(() => done()); }); }); diff --git a/src/routes/workStreams/delete.js b/src/routes/workStreams/delete.js index aa2ef463..12b4f046 100644 --- a/src/routes/workStreams/delete.js +++ b/src/routes/workStreams/delete.js @@ -19,7 +19,7 @@ module.exports = [ validate(schema), permissions('workStream.delete'), (req, res, next) => - models.sequelize.transaction(() => + models.sequelize.transaction(() => models.WorkStream.findOne({ where: { id: req.params.id, @@ -37,8 +37,8 @@ module.exports = [ return entity.update({ deletedBy: req.authUser.userId }); }) .then(entity => entity.destroy())) - .then(() => { - res.status(204).end(); - }) - .catch(next), + .then(() => { + res.status(204).end(); + }) + .catch(next), ]; diff --git a/src/routes/workStreams/delete.spec.js b/src/routes/workStreams/delete.spec.js index 5a3b92b6..165430da 100644 --- a/src/routes/workStreams/delete.spec.js +++ b/src/routes/workStreams/delete.spec.js @@ -10,27 +10,27 @@ import testUtil from '../../tests/util'; const expectAfterDelete = (id, projectId, err, next) => { if (err) throw err; setTimeout(() => - models.WorkStream.findOne({ - where: { - id, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.WorkStream.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${projectId}/workstreams/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/${projectId}/workstreams/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE work stream', () => { @@ -68,20 +68,20 @@ describe('DELETE work stream', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - id = entity.id; - done(); + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + id = entity.id; + done(); + }); }); - }); }); }); }); diff --git a/src/routes/workStreams/get.spec.js b/src/routes/workStreams/get.spec.js index d696da90..770d3d2d 100644 --- a/src/routes/workStreams/get.spec.js +++ b/src/routes/workStreams/get.spec.js @@ -46,21 +46,21 @@ describe('GET work stream', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - id = entity.id; - workStream = entity; - done(); + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + id = entity.id; + workStream = entity; + done(); + }); }); - }); }); }); }); diff --git a/src/routes/workStreams/list.js b/src/routes/workStreams/list.js index c536e2b9..f865d11d 100644 --- a/src/routes/workStreams/list.js +++ b/src/routes/workStreams/list.js @@ -24,22 +24,22 @@ module.exports = [ id: projectId, }, }) - .then((countProject) => { - if (countProject === 0) { - const apiErr = new Error(`active project not found for project id ${projectId}`); - apiErr.status = 404; - throw apiErr; - } + .then((countProject) => { + if (countProject === 0) { + const apiErr = new Error(`active project not found for project id ${projectId}`); + apiErr.status = 404; + throw apiErr; + } - return models.WorkStream.findAll({ - where: { - projectId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }); - }) - .then(workStreams => res.json(workStreams)) - .catch(next); + return models.WorkStream.findAll({ + where: { + projectId, + }, + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }); + }) + .then(workStreams => res.json(workStreams)) + .catch(next); }, ]; diff --git a/src/routes/workStreams/list.spec.js b/src/routes/workStreams/list.spec.js index 371ccc95..3794f59a 100644 --- a/src/routes/workStreams/list.spec.js +++ b/src/routes/workStreams/list.spec.js @@ -59,10 +59,10 @@ describe('LIST work streams', () => { lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.bulkCreate(_.map(workStreams, w => _.assign(w, { projectId }))).then(() => done()); - }); + .then((project) => { + projectId = project.id; + models.WorkStream.bulkCreate(_.map(workStreams, w => _.assign(w, { projectId }))).then(() => done()); + }); }); }); }); diff --git a/src/routes/workStreams/update.js b/src/routes/workStreams/update.js index e370c80a..4094c933 100644 --- a/src/routes/workStreams/update.js +++ b/src/routes/workStreams/update.js @@ -45,17 +45,17 @@ module.exports = [ projectId, }, }) - .then((workStream) => { - if (!workStream) { + .then((workStream) => { + if (!workStream) { // handle 404 - const err = new Error(`work stream not found for project id ${projectId} ` + + const err = new Error(`work stream not found for project id ${projectId} ` + `and work stream id ${workStreamId}`); - err.status = 404; - return Promise.reject(err); - } + err.status = 404; + return Promise.reject(err); + } - return workStream.update(entityToUpdate); - }) + return workStream.update(entityToUpdate); + }) .then((workStream) => { res.json(workStream); return Promise.resolve(); diff --git a/src/routes/workStreams/update.spec.js b/src/routes/workStreams/update.spec.js index 96c074f5..6b4bb854 100644 --- a/src/routes/workStreams/update.spec.js +++ b/src/routes/workStreams/update.spec.js @@ -32,52 +32,52 @@ describe('UPDATE Work Stream', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'workStream.edit', - permission: { - allowRule: { projectRoles: ['manager', 'copilot'], topcoderRoles: ['Connect Admin', 'administrator'] }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create project - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'workStream.edit', + permission: { + allowRule: { projectRoles: ['manager', 'copilot'], topcoderRoles: ['Connect Admin', 'administrator'] }, + denyRule: { projectRoles: ['copilot'] }, + }, + projectTemplateId: template.id, details: {}, createdBy: 1, updatedBy: 1, lastActivityAt: 1, lastActivityUserId: '1', }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - id = entity.id; - workStream = entity; - done(); + .then(() => { + // Create project + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + id = entity.id; + workStream = entity; + done(); + }); + }); }); - }); }); - }); }); }); diff --git a/src/routes/works/create.js b/src/routes/works/create.js index a1d3cf5e..89fa0fe1 100644 --- a/src/routes/works/create.js +++ b/src/routes/works/create.js @@ -134,26 +134,26 @@ module.exports = [ }); }), ) - .then(() => { + .then(() => { // Send events to buses - req.log.debug('Sending event to RabbitMQ bus for project phase %d', newProjectPhase.id); - req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, - { added: newProjectPhase, route: TIMELINE_REFERENCES.WORK }, - { correlationId: req.id }, - ); + req.log.debug('Sending event to RabbitMQ bus for project phase %d', newProjectPhase.id); + req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, + { added: newProjectPhase, route: TIMELINE_REFERENCES.WORK }, + { correlationId: req.id }, + ); - req.log.debug('Sending event to Kafka bus for project phase %d', newProjectPhase.id); - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, - RESOURCES.PHASE, - newProjectPhase, - ); + req.log.debug('Sending event to Kafka bus for project phase %d', newProjectPhase.id); + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, + RESOURCES.PHASE, + newProjectPhase, + ); - res.status(201).json(newProjectPhase); - }) - .catch((err) => { - util.handleError('Error creating work', err, req, next); - }); + res.status(201).json(newProjectPhase); + }) + .catch((err) => { + util.handleError('Error creating work', err, req, next); + }); }, ]; diff --git a/src/routes/works/create.spec.js b/src/routes/works/create.spec.js index 0d3c6d67..2c85ae01 100644 --- a/src/routes/works/create.spec.js +++ b/src/routes/works/create.spec.js @@ -79,93 +79,93 @@ describe('CREATE work', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'work.create', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'work.create', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create(_.assign(project, { templateId: template.id })) - .then((_project) => { - projectId = _project.id; - projectName = _project.name; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, - projectId, - role: 'customer', - isPrimary: true, - createdBy: 1, - updatedBy: 1, - }]) - .then(() => - models.ProductTemplate.create({ - name: 'name 1', - productKey: 'productKey 1', - category: 'generic', - subCategory: 'generic', - icon: 'http://example.com/icon1.ico', - brief: 'brief 1', - details: 'details 1', - aliases: ['product key 1', 'product_key_1'], - template: { - template1: { - name: 'template 1', - details: { - anyDetails: 'any details 1', - }, - others: ['others 11', 'others 12'], - }, - template2: { - name: 'template 2', - details: { - anyDetails: 'any details 2', - }, - others: ['others 21', 'others 22'], - }, - }, - createdBy: 1, - updatedBy: 2, - }).then((productTemplate) => { - productTemplateId = productTemplate.id; - done(); - }), - ); + projectTemplateId: template.id, + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then(() => { + // Create projects + models.Project.create(_.assign(project, { templateId: template.id })) + .then((_project) => { + projectId = _project.id; + projectName = _project.name; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]) + .then(() => + models.ProductTemplate.create({ + name: 'name 1', + productKey: 'productKey 1', + category: 'generic', + subCategory: 'generic', + icon: 'http://example.com/icon1.ico', + brief: 'brief 1', + details: 'details 1', + aliases: ['product key 1', 'product_key_1'], + template: { + template1: { + name: 'template 1', + details: { + anyDetails: 'any details 1', + }, + others: ['others 11', 'others 12'], + }, + template2: { + name: 'template 2', + details: { + anyDetails: 'any details 2', + }, + others: ['others 21', 'others 22'], + }, + }, + createdBy: 1, + updatedBy: 2, + }).then((productTemplate) => { + productTemplateId = productTemplate.id; + done(); + }), + ); + }); + }); }); - }); }); - }); }); }); @@ -339,42 +339,42 @@ describe('CREATE work', () => { it('should send correct BUS API messages when work added', (done) => { request(server) - .post(`/v5/projects/${projectId}/workstreams/${workStreamId}/works`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send(body) - .expect('Content-Type', /json/) - .expect(201) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ - resource: RESOURCES.PHASE, - name: body.name, - status: body.status, - budget: body.budget, - progress: body.progress, - projectId, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051331, - initiatorUserId: 40051331, - })).should.be.true; - - done(); - }); - } - }); + .post(`/v5/projects/${projectId}/workstreams/${workStreamId}/works`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_CREATED, sinon.match({ + resource: RESOURCES.PHASE, + name: body.name, + status: body.status, + budget: body.budget, + progress: body.progress, + projectId, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051331, + initiatorUserId: 40051331, + })).should.be.true; + + done(); + }); + } + }); }); }); diff --git a/src/routes/works/delete.js b/src/routes/works/delete.js index 885c8c5b..9491e5db 100644 --- a/src/routes/works/delete.js +++ b/src/routes/works/delete.js @@ -25,57 +25,57 @@ module.exports = [ (req, res, next) => { const projectId = req.params.projectId; models.sequelize.transaction(() => - models.PhaseWorkStream.findOne({ - where: { - phaseId: req.params.id, - workStreamId: req.params.workStreamId, - }, - }) - .then((work) => { - // Not found - if (!work) { - const apiErr = new Error(`work not found for work stream id ${req.params.workStreamId} ` + - `and work id ${req.params.id}`); - apiErr.status = 404; - throw apiErr; - } - - return models.ProjectPhase.findOne({ + models.PhaseWorkStream.findOne({ where: { - id: req.params.id, - projectId, + phaseId: req.params.id, + workStreamId: req.params.workStreamId, }, - }); - }) - .then((entity) => { - if (!entity) { - const apiErr = new Error(`work not found for work stream id ${req.params.workStreamId}, ` + + }) + .then((work) => { + // Not found + if (!work) { + const apiErr = new Error(`work not found for work stream id ${req.params.workStreamId} ` + + `and work id ${req.params.id}`); + apiErr.status = 404; + throw apiErr; + } + + return models.ProjectPhase.findOne({ + where: { + id: req.params.id, + projectId, + }, + }); + }) + .then((entity) => { + if (!entity) { + const apiErr = new Error(`work not found for work stream id ${req.params.workStreamId}, ` + `project id ${projectId} and work id ${req.params.id}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - // Update the deletedBy, then delete - return entity.update({ deletedBy: req.authUser.userId }); - }) - .then(entity => entity.destroy())) - .then((deleted) => { - req.log.debug('deleted work', JSON.stringify(deleted, null, 2)); + apiErr.status = 404; + return Promise.reject(apiErr); + } + // Update the deletedBy, then delete + return entity.update({ deletedBy: req.authUser.userId }); + }) + .then(entity => entity.destroy())) + .then((deleted) => { + req.log.debug('deleted work', JSON.stringify(deleted, null, 2)); - // Send events to buses - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_PHASE_REMOVED, - { deleted, route: TIMELINE_REFERENCES.WORK }, - { correlationId: req.id }, - ); + // Send events to buses + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_PHASE_REMOVED, + { deleted, route: TIMELINE_REFERENCES.WORK }, + { correlationId: req.id }, + ); - // emit event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_PHASE_REMOVED, - RESOURCES.PHASE, - _.pick(deleted.toJSON(), 'id')); + // emit event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_PHASE_REMOVED, + RESOURCES.PHASE, + _.pick(deleted.toJSON(), 'id')); - res.status(204).json({}); - }).catch(err => next(err)); + res.status(204).json({}); + }).catch(err => next(err)); }, ]; diff --git a/src/routes/works/delete.spec.js b/src/routes/works/delete.spec.js index e1ed3bab..a45644ad 100644 --- a/src/routes/works/delete.spec.js +++ b/src/routes/works/delete.spec.js @@ -24,27 +24,27 @@ chai.should(); const expectAfterDelete = (workId, projectId, workStreamId, err, next) => { if (err) throw err; setTimeout(() => - models.ProjectPhase.findOne({ - where: { - id: workId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + models.ProjectPhase.findOne({ + where: { + id: workId, + }, + paranoid: false, + }) + .then((res) => { + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }), 500); }; describe('DELETE work', () => { @@ -104,81 +104,81 @@ describe('DELETE work', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'work.delete', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'work.delete', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create(_.assign(project, { templateId: template.id })) - .then((_project) => { - projectId = _project.id; - projectName = _project.name; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }).then(() => { - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, + projectTemplateId: template.id, + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then(() => { + // Create projects + models.Project.create(_.assign(project, { templateId: template.id })) + .then((_project) => { + projectId = _project.id; + projectName = _project.name; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', projectId, - role: 'customer', - isPrimary: true, createdBy: 1, updatedBy: 1, - }]).then(() => done()); + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, + createdBy: 1, + updatedBy: 1, + projectId, + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }).then(() => { + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); + }); + }); + }); }); - }); }); - }); }); - }); }); }); @@ -283,36 +283,36 @@ describe('DELETE work', () => { it('should send correct BUS API messages when work removed', (done) => { request(server) - .delete(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, - }) - .expect(204) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051336, - initiatorUserId: 40051336, - })).should.be.true; - - done(); - }); - } - }); + .delete(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_DELETED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051336, + initiatorUserId: 40051336, + })).should.be.true; + + done(); + }); + } + }); }); }); diff --git a/src/routes/works/get.spec.js b/src/routes/works/get.spec.js index 768fd785..ff25805e 100644 --- a/src/routes/works/get.spec.js +++ b/src/routes/works/get.spec.js @@ -45,55 +45,55 @@ describe('GET work', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { + .then((template) => { // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then((project) => { - projectId = project.id; - models.WorkStream.create({ - name: 'Work Stream', + models.Project.create({ type: 'generic', - status: 'active', - projectId, + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, createdBy: 1, updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - models.ProjectPhase.create({ - name: 'test project phase', - status: 'active', - startDate: '2018-05-15T00:00:00Z', - endDate: '2018-05-15T12:00:00Z', - budget: 20.0, - progress: 1.23456, - details: { - message: 'This can be any json', - }, - createdBy: 1, - updatedBy: 1, - projectId, - }).then((phase) => { - workId = phase.id; - models.PhaseWorkStream.create({ - phaseId: workId, - workStreamId, - }).then(() => done()); + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then((project) => { + projectId = project.id; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', + projectId, + createdBy: 1, + updatedBy: 1, + }).then((entity) => { + workStreamId = entity.id; + models.ProjectPhase.create({ + name: 'test project phase', + status: 'active', + startDate: '2018-05-15T00:00:00Z', + endDate: '2018-05-15T12:00:00Z', + budget: 20.0, + progress: 1.23456, + details: { + message: 'This can be any json', + }, + createdBy: 1, + updatedBy: 1, + projectId, + }).then((phase) => { + workId = phase.id; + models.PhaseWorkStream.create({ + phaseId: workId, + workStreamId, + }).then(() => done()); + }); + }); }); - }); }); - }); }); }); diff --git a/src/routes/works/list.js b/src/routes/works/list.js index 78f00f03..2f797fde 100644 --- a/src/routes/works/list.js +++ b/src/routes/works/list.js @@ -71,26 +71,26 @@ module.exports = [ include: [include], order: [[models.ProjectPhase, sortParameters[0], sortParameters[1]]], }) - .then((existingWorkStream) => { - if (!existingWorkStream) { + .then((existingWorkStream) => { + if (!existingWorkStream) { // handle 404 - const err = new Error(`active work stream not found for project id ${projectId} ` + + const err = new Error(`active work stream not found for project id ${projectId} ` + `and work stream id ${workStreamId}`); - err.status = 404; - throw err; - } - - // rename 'products' to 'workItems' - return existingWorkStream.ProjectPhases.map((phase) => { - const phaseObj = phase.get({ plain: true }); - if (_.has(phaseObj, 'products')) { - _.set(phaseObj, 'workItems', _.get(phaseObj, 'products')); - _.unset(phaseObj, 'products'); + err.status = 404; + throw err; } - return phaseObj; - }); - }) - .then(phases => res.json(phases)) - .catch(next); + + // rename 'products' to 'workItems' + return existingWorkStream.ProjectPhases.map((phase) => { + const phaseObj = phase.get({ plain: true }); + if (_.has(phaseObj, 'products')) { + _.set(phaseObj, 'workItems', _.get(phaseObj, 'products')); + _.unset(phaseObj, 'products'); + } + return phaseObj; + }); + }) + .then(phases => res.json(phases)) + .catch(next); }, ]; diff --git a/src/routes/works/list.spec.js b/src/routes/works/list.spec.js index 4edc1796..859a89c3 100644 --- a/src/routes/works/list.spec.js +++ b/src/routes/works/list.spec.js @@ -59,35 +59,35 @@ describe('LIST works', () => { beforeEach((done) => { testUtil.clearDb() - .then(() => { - models.ProjectTemplate.create({ - name: 'template 2', - key: 'key 2', - category: 'category 2', - icon: 'http://example.com/icon1.ico', - question: 'question 2', - info: 'info 2', - aliases: ['key-2', 'key_2'], - scope: {}, - phases: {}, - createdBy: 1, - updatedBy: 2, - }) - .then((template) => { - // Create projects - models.Project.create({ - type: 'generic', - billingAccountId: 1, - name: 'test1', - description: 'test project1', - status: 'draft', - templateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) + .then(() => { + models.ProjectTemplate.create({ + name: 'template 2', + key: 'key 2', + category: 'category 2', + icon: 'http://example.com/icon1.ico', + question: 'question 2', + info: 'info 2', + aliases: ['key-2', 'key_2'], + scope: {}, + phases: {}, + createdBy: 1, + updatedBy: 2, + }) + .then((template) => { + // Create projects + models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + templateId: template.id, + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }) .then((project) => { projectId = project.id; models.WorkStream.create({ @@ -118,8 +118,8 @@ describe('LIST works', () => { }); }); }); - }); - }); + }); + }); }); after((done) => { diff --git a/src/routes/works/update.js b/src/routes/works/update.js index 0dd343a8..8550f821 100644 --- a/src/routes/works/update.js +++ b/src/routes/works/update.js @@ -66,41 +66,41 @@ module.exports = [ }, }], }) - .then((existing) => { - if (!existing) { + .then((existing) => { + if (!existing) { // handle 404 - const err = new Error('No active project phase found for project id ' + + const err = new Error('No active project phase found for project id ' + `${projectId} and work stream ${workStreamId} and phase id ${phaseId}`); - err.status = 404; - throw err; - } else { - previousValue = _.clone(existing.get({ plain: true })); - - // make sure startDate < endDate - let startDate; - let endDate; - if (updatedProps.startDate) { - startDate = new Date(updatedProps.startDate); - } else { - startDate = existing.startDate !== null ? new Date(existing.startDate) : null; - } - - if (updatedProps.endDate) { - endDate = new Date(updatedProps.endDate); - } else { - endDate = existing.endDate !== null ? new Date(existing.endDate) : null; - } - - if (startDate !== null && endDate !== null && startDate > endDate) { - const err = new Error('startDate must not be after endDate.'); - err.status = 400; + err.status = 404; throw err; } else { - _.extend(existing, updatedProps); - return existing.save().catch(next); + previousValue = _.clone(existing.get({ plain: true })); + + // make sure startDate < endDate + let startDate; + let endDate; + if (updatedProps.startDate) { + startDate = new Date(updatedProps.startDate); + } else { + startDate = existing.startDate !== null ? new Date(existing.startDate) : null; + } + + if (updatedProps.endDate) { + endDate = new Date(updatedProps.endDate); + } else { + endDate = existing.endDate !== null ? new Date(existing.endDate) : null; + } + + if (startDate !== null && endDate !== null && startDate > endDate) { + const err = new Error('startDate must not be after endDate.'); + err.status = 400; + throw err; + } else { + _.extend(existing, updatedProps); + return existing.save().catch(next); + } } - } - }) + }) .then((updatedPhase) => { updated = updatedPhase; // Ignore re-ordering if there's no order specified for this phase diff --git a/src/routes/works/update.spec.js b/src/routes/works/update.spec.js index ce4301b1..cf79077a 100644 --- a/src/routes/works/update.spec.js +++ b/src/routes/works/update.spec.js @@ -119,82 +119,82 @@ describe('UPDATE work', () => { createdBy: 1, updatedBy: 2, }) - .then((template) => { - models.WorkManagementPermission.create({ - policy: 'work.edit', - permission: { - allowRule: { - projectRoles: ['customer', 'copilot'], - topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + .then((template) => { + models.WorkManagementPermission.create({ + policy: 'work.edit', + permission: { + allowRule: { + projectRoles: ['customer', 'copilot'], + topcoderRoles: ['Connect Manager', 'Connect Admin', 'administrator'], + }, + denyRule: { projectRoles: ['copilot'] }, }, - denyRule: { projectRoles: ['copilot'] }, - }, - projectTemplateId: template.id, - details: {}, - createdBy: 1, - updatedBy: 1, - lastActivityAt: 1, - lastActivityUserId: '1', - }) - .then(() => { - // Create projects - models.Project.create(_.assign(project, { templateId: template.id })) - .then((_project) => { - projectId = _project.id; - projectName = _project.name; - models.WorkStream.create({ - name: 'Work Stream', - type: 'generic', - status: 'active', - projectId, - createdBy: 1, - updatedBy: 1, - }).then((entity) => { - workStreamId = entity.id; - _.assign(body, { projectId }); - const createPhases = [ - body, - _.assign({ order: 1 }, body), - _.assign({}, body, { status: 'draft' }), - ]; - models.ProjectPhase.bulkCreate(createPhases, { returning: true }).then((phases) => { - workId = phases[0].id; - workId2 = phases[1].id; - workId3 = phases[2].id; - models.PhaseWorkStream.bulkCreate([{ - phaseId: phases[0].id, - workStreamId, - }, { - phaseId: phases[1].id, - workStreamId, - }, { - phaseId: phases[2].id, - workStreamId, - }]).then(() => { - // create members - models.ProjectMember.bulkCreate([{ - id: 1, - userId: copilotUser.userId, - projectId, - role: 'copilot', - isPrimary: false, - createdBy: 1, - updatedBy: 1, - }, { - id: 2, - userId: memberUser.userId, + projectTemplateId: template.id, + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }) + .then(() => { + // Create projects + models.Project.create(_.assign(project, { templateId: template.id })) + .then((_project) => { + projectId = _project.id; + projectName = _project.name; + models.WorkStream.create({ + name: 'Work Stream', + type: 'generic', + status: 'active', projectId, - role: 'customer', - isPrimary: true, createdBy: 1, updatedBy: 1, - }]).then(() => done()); + }).then((entity) => { + workStreamId = entity.id; + _.assign(body, { projectId }); + const createPhases = [ + body, + _.assign({ order: 1 }, body), + _.assign({}, body, { status: 'draft' }), + ]; + models.ProjectPhase.bulkCreate(createPhases, { returning: true }).then((phases) => { + workId = phases[0].id; + workId2 = phases[1].id; + workId3 = phases[2].id; + models.PhaseWorkStream.bulkCreate([{ + phaseId: phases[0].id, + workStreamId, + }, { + phaseId: phases[1].id, + workStreamId, + }, { + phaseId: phases[2].id, + workStreamId, + }]).then(() => { + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); + }); + }); + }); }); - }); }); - }); }); - }); }); }); @@ -372,338 +372,338 @@ describe('UPDATE work', () => { it('should send correct BUS API messages when spentBudget updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - spentBudget: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + spentBudget: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PAYMENT).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PAYMENT).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when progress updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - progress: 50, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(3); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PROGRESS).should.be.true; - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED).should.be.true; - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + progress: 50, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(3); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_PROGRESS).should.be.true; + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PROGRESS_MODIFIED).should.be.true; + done(); + }); + } + }); }); it('should send correct BUS API messages when details updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - details: { - text: 'something', - }, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + details: { + text: 'something', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_SCOPE).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_UPDATE_SCOPE).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when status updated (completed)', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - status: 'completed', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + status: 'completed', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_COMPLETED).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_COMPLETED).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when status updated (active)', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId3}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - status: 'active', - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId3}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + status: 'active', + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId3, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId3, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_ACTIVE).should.be.true; + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_WORK_TRANSITION_ACTIVE).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when budget updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - budget: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + budget: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when startDate updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - startDate: 123, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; - - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + startDate: 123, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; + + done(); + }); + } + }); }); it('should send correct BUS API messages when duration updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - duration: 100, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(2); - - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - duration: 100, - })).should.be.true; - - // Check Notification Service events - createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051333, - initiatorUserId: 40051333, - })).should.be.true; - - done(); - }); - } - }); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + duration: 100, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(2); + + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + duration: 100, + })).should.be.true; + + // Check Notification Service events + createEventSpy.calledWith(CONNECT_NOTIFICATION_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051333, + initiatorUserId: 40051333, + })).should.be.true; + + done(); + }); + } + }); }); it('should send correct BUS API messages when order updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - order: 100, - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + order: 100, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - // NOTE: no other event should be called, as this phase doesn't move any other phases + // NOTE: no other event should be called, as this phase doesn't move any other phases - done(); - }); - } - }); + done(); + }); + } + }); }); it('should send correct BUS API messages when endDate updated', (done) => { request(server) - .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send({ - endDate: new Date(), - }) - .expect('Content-Type', /json/) - .expect(200) - .end((err) => { - if (err) { - done(err); - } else { - testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(1); + .patch(`/v5/projects/${projectId}/workstreams/${workStreamId}/works/${workId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + endDate: new Date(), + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(1); - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ - resource: RESOURCES.PHASE, - id: workId, - updatedBy: testUtil.userIds.admin, - })).should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATED, sinon.match({ + resource: RESOURCES.PHASE, + id: workId, + updatedBy: testUtil.userIds.admin, + })).should.be.true; - done(); - }); - } - }); + done(); + }); + } + }); }); }); diff --git a/src/services/index.js b/src/services/index.js index 6c8306c8..ff13ac32 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -18,7 +18,7 @@ module.exports = (fapp, logger) => { const app = fapp; app.services = app.service || {}; if (process.env.NODE_ENV.toLowerCase() === 'test') { - require('../tests/serviceMocks')(app); // eslint-disable-line global-require + require('../tests/serviceMocks')(app); // eslint-disable-line global-require } else { logger.info('initializing RabbitMQ service'); // RabbitMQ Initialization @@ -30,16 +30,16 @@ module.exports = (fapp, logger) => { config.get('pubsubExchangeName'), config.get('pubsubQueueName'), ) - .then(() => { - logger.info('RabbitMQ service initialized'); - }) + .then(() => { + logger.info('RabbitMQ service initialized'); + }) // .then(() => startKafkaConsumer(kafkaHandlers, app, logger)) // .then(() => { // logger.info('Kafka consumer service initialized'); // }) - .catch((err) => { - logger.error('Error initializing services', err); + .catch((err) => { + logger.error('Error initializing services', err); // gracefulShutdown() - }); + }); } }; diff --git a/src/services/messageService.js b/src/services/messageService.js index f1ffc384..dc429fce 100644 --- a/src/services/messageService.js +++ b/src/services/messageService.js @@ -149,14 +149,14 @@ function getTopicByTag(projectId, tag, logger) { logger.debug(`calling message service for fetching ${tag}`); const encodedFilter = encodeURIComponent(`reference=project&referenceId=${projectId}&tag=${tag}`); return msgClient.get(`/topics/list/db?filter=${encodedFilter}`) - .then((resp) => { - const topics = _.get(resp.data, 'result.content', []); - logger.debug(`Fetched ${topics.length} topics`); - if (topics && topics.length > 0) { - return topics[0]; - } - return null; - }); + .then((resp) => { + const topics = _.get(resp.data, 'result.content', []); + logger.debug(`Fetched ${topics.length} topics`); + if (topics && topics.length > 0) { + return topics[0]; + } + return null; + }); }); } diff --git a/src/tests/util.js b/src/tests/util.js index 4983280f..26caf1d0 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -8,13 +8,13 @@ const jwt = require('jsonwebtoken'); export default { clearDb: done => models.sequelize.sync({ force: true }) - .then(() => { - if (done) done(); - }), + .then(() => { + if (done) done(); + }), clearES: done => elasticsearchSync.sync() - .then(() => { - if (done) done(); - }), + .then(() => { + if (done) done(); + }), mockHttpClient: { defaults: { headers: { common: {} } }, interceptors: { response: { use: () => {} } }, diff --git a/src/util.js b/src/util.js index 753afae4..981dea0a 100644 --- a/src/util.js +++ b/src/util.js @@ -116,9 +116,9 @@ const projectServiceUtils = { */ calculateProjectEstimationItems: (req, projectId) => // delete ALL existent ProjectEstimationItems for the project - models.ProjectEstimationItem.deleteAllForProject(models, projectId, req.authUser, { - includeAllProjectEstimatinoItemsForInternalUsage: true, - }) + models.ProjectEstimationItem.deleteAllForProject(models, projectId, req.authUser, { + includeAllProjectEstimatinoItemsForInternalUsage: true, + }) // retrieve ProjectSettings and ProjectEstimations .then(() => Promise.all([ @@ -486,12 +486,12 @@ const projectServiceUtils = { } const data = query ? (yield esClient.search({ index: INDEX, type: TYPE, body: query })) : - (yield esClient.search({ index: INDEX, type: TYPE })); + (yield esClient.search({ index: INDEX, type: TYPE })); if (data.hits.hits.length > 0 && data.hits.hits[0].inner_hits) { return data.hits.hits[0].inner_hits; } - return data.hits.hits.length > 0 ? data.hits.hits[0]._source : { // eslint-disable-line no-underscore-dangle + return data.hits.hits.length > 0 ? data.hits.hits[0]._source : { // eslint-disable-line no-underscore-dangle productTemplates: [], forms: [], projectTemplates: [], @@ -807,7 +807,7 @@ const projectServiceUtils = { Authorization: `Bearer ${token}`, }, }).then(res => _.get(res, 'data.result.content', []) - .map(r => r.roleName)); + .map(r => r.roleName)); } catch (err) { return Promise.reject(err); } @@ -829,7 +829,7 @@ const projectServiceUtils = { } }), - /** + /** * Send resource to kafka bus * @param {object} req Request object * @param {String} key the event key @@ -840,17 +840,17 @@ const projectServiceUtils = { * @param {Boolean}[skipNotification] if true, than event is not send to Notification Service */ sendResourceToKafkaBus: Promise.coroutine(function* (req, key, name, resource, originalResource, route, skipNotification) { // eslint-disable-line - req.log.debug('Sending event to Kafka bus for resource %s %s', name, resource.id || resource.key); - - // emit event - req.app.emit(key, { - req, - resource: _.assign({ resource: name }, resource), - originalResource: originalResource ? _.assign({ resource: name }, originalResource) : undefined, - route, - skipNotification, - }); - }), + req.log.debug('Sending event to Kafka bus for resource %s %s', name, resource.id || resource.key); + + // emit event + req.app.emit(key, { + req, + resource: _.assign({ resource: name }, resource), + originalResource: originalResource ? _.assign({ resource: name }, originalResource) : undefined, + route, + skipNotification, + }); + }), /** * Add userId to project @@ -874,44 +874,44 @@ const projectServiceUtils = { // register member return models.ProjectMember.create(member, { transaction }) - .then((_newMember) => { - newMember = _newMember.get({ plain: true }); - - // we have to remove all pending invites for the member if any, as we can add a member directly without invite - return models.ProjectMemberInvite.getPendingInviteByEmailOrUserId(member.projectId, null, newMember.userId) - .then((invite) => { - if (invite) { - return invite.update({ - status: INVITE_STATUS.CANCELED, - }, { - transaction, - }); - } - - return Promise.resolve(); - }).then(() => { + .then((_newMember) => { + newMember = _newMember.get({ plain: true }); + + // we have to remove all pending invites for the member if any, as we can add a member directly without invite + return models.ProjectMemberInvite.getPendingInviteByEmailOrUserId(member.projectId, null, newMember.userId) + .then((invite) => { + if (invite) { + return invite.update({ + status: INVITE_STATUS.CANCELED, + }, { + transaction, + }); + } + + return Promise.resolve(); + }).then(() => { // TODO Should we also send Kafka event in case we removed some invite above? - // publish event - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_MEMBER_ADDED, - newMember, - { correlationId: req.id }, - ); - // emit the event - util.sendResourceToKafkaBus( - req, - EVENT.ROUTING_KEY.PROJECT_MEMBER_ADDED, - RESOURCES.PROJECT_MEMBER, - newMember); + // publish event + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_MEMBER_ADDED, + newMember, + { correlationId: req.id }, + ); + // emit the event + util.sendResourceToKafkaBus( + req, + EVENT.ROUTING_KEY.PROJECT_MEMBER_ADDED, + RESOURCES.PROJECT_MEMBER, + newMember); return newMember; - }); - }) - .catch((err) => { - req.log.error('Unable to register ', err); - return Promise.reject(err); - }); + }); + }) + .catch((err) => { + req.log.error('Unable to register ', err); + return Promise.reject(err); + }); }), /** @@ -929,29 +929,29 @@ const projectServiceUtils = { } req.log.trace('filter for users api call', filter); return util.getM2MToken() - .then((token) => { - req.log.debug(`Bearer ${token}`); - const httpClient = util.getHttpClient({ id: req.id, log: req.log }); - return httpClient.get(`${config.get('identityServiceEndpoint')}users`, { - headers: { - Authorization: `Bearer ${token}`, - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - params: { - fields: 'handle,id,email', - filter, - }, - // set longer timeout as default 3000 could be not enough for identity service response - timeout: 15000, - }) - .then((response) => { - const data = _.get(response, 'data.result.content', null); - if (!data) { throw new Error('Response does not have result.content'); } - req.log.debug('UserHandle response', data); - return data; + .then((token) => { + req.log.debug(`Bearer ${token}`); + const httpClient = util.getHttpClient({ id: req.id, log: req.log }); + return httpClient.get(`${config.get('identityServiceEndpoint')}users`, { + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + params: { + fields: 'handle,id,email', + filter, + }, + // set longer timeout as default 3000 could be not enough for identity service response + timeout: 15000, + }) + .then((response) => { + const data = _.get(response, 'data.result.content', null); + if (!data) { throw new Error('Response does not have result.content'); } + req.log.debug('UserHandle response', data); + return data; + }); }); - }); }, /** @@ -999,7 +999,7 @@ const projectServiceUtils = { const start = batch * maximumRequests; const end = (batch + 1) * maximumRequests; const requests = emails.slice(start, end).map(userEmail => - generateRequest({ token, email: userEmail })); + generateRequest({ token, email: userEmail })); return Promise.all(requests) .then((responses) => { const data = responses.reduce((contents, response) => { @@ -1044,22 +1044,22 @@ const projectServiceUtils = { setPaginationHeaders: (req, res, data) => { const totalPages = Math.ceil(data.count / data.pageSize); let fullUrl = `${req.protocol}://${req.get('host')}${req.url.replace(`&page=${data.page}`, '')}`; - // URL formatting to add pagination parameters accordingly + // URL formatting to add pagination parameters accordingly if (fullUrl.indexOf('?') === -1) { fullUrl += '?'; } else { fullUrl += '&'; } - // Pagination follows github style + // Pagination follows github style if (data.count > 0) { // Set Pagination headers only if there is data to paginate let link = ''; // Content for Link header - // Set first and last page in Link header + // Set first and last page in Link header link += `<${fullUrl}page=1>; rel="first"`; link += `, <${fullUrl}page=${totalPages}>; rel="last"`; - // Set Prev-Page only if it's not first page and within page limits + // Set Prev-Page only if it's not first page and within page limits if (data.page > 1 && data.page <= totalPages) { const prevPage = (data.page - 1); res.set({ @@ -1068,7 +1068,7 @@ const projectServiceUtils = { link += `, <${fullUrl}page=${prevPage}>; rel="prev"`; } - // Set Next-Page only if it's not Last page and within page limits + // Set Next-Page only if it's not Last page and within page limits if (data.page < totalPages) { const nextPage = (_.parseInt(data.page) + 1); res.set({ @@ -1077,7 +1077,7 @@ const projectServiceUtils = { link += `, <${fullUrl}page=${nextPage}>; rel="next"`; } - // Allow browsers access pagination data in headers + // Allow browsers access pagination data in headers let accessControlExposeHeaders = res.get('Access-Control-Expose-Headers') || ''; accessControlExposeHeaders += accessControlExposeHeaders ? ', ' : ''; accessControlExposeHeaders += 'X-Page, X-Per-Page, X-Total, X-Total-Pages'; @@ -1091,7 +1091,7 @@ const projectServiceUtils = { Link: link, }); } - // Return the data after setting pagination headers + // Return the data after setting pagination headers res.json(data.rows); }, @@ -1309,6 +1309,8 @@ const projectServiceUtils = { * Check if permission requires us to provide the list Project Members or no. * * @param {Object} permission permission or permissionRule + * + * @return {Boolean} true if has permission */ isPermissionRequireProjectMembers: (permission) => { if (!permission) {