From 2d0f5cc33bec06cbd2bd641362ae74c4f2e13cc6 Mon Sep 17 00:00:00 2001 From: Gian Franco Zabarino Date: Sat, 14 Jul 2018 03:33:17 -0300 Subject: [PATCH 01/77] Added most needed events. --- src/constants.js | 10 ++++++++++ src/events/busApi.js | 26 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/constants.js b/src/constants.js index 4e24c806..d0139de2 100644 --- a/src/constants.js +++ b/src/constants.js @@ -61,6 +61,12 @@ export const BUS_API_EVENT = { PROJECT_CANCELED: 'notifications.connect.project.canceled', PROJECT_ACTIVE: 'notifications.connect.project.active', + PROJECT_PHASE_TRANSITION_ACTIVE: 'project.phase.transition.active', + PROJECT_PHASE_TRANSITION_COMPLETED: 'project.phase.transition.completed', + PROJECT_PHASE_UPDATE_PAYMENT: 'project.phase.update.payment', + PROJECT_PHASE_UPDATE_PROGRESS: 'project.phase.update.progress', + PROJECT_PHASE_UPDATE_SCOPE: 'project.phase.update.scope', + MEMBER_JOINED: 'notifications.connect.project.member.joined', MEMBER_LEFT: 'notifications.connect.project.member.left', MEMBER_REMOVED: 'notifications.connect.project.member.removed', @@ -71,12 +77,16 @@ export const BUS_API_EVENT = { PROJECT_LINK_CREATED: 'notifications.connect.project.linkCreated', PROJECT_FILE_UPLOADED: 'notifications.connect.project.fileUploaded', PROJECT_SPECIFICATION_MODIFIED: 'notifications.connect.project.specificationModified', + PROJECT_PROGRESS_MODIFIED: 'notifications.connect.project.progressModified', // When phase is added/updated/deleted from the project, // When product is added/deleted from a phase // When product is updated on any field other than specification PROJECT_PLAN_MODIFIED: 'notifications.connect.project.planModified', + // TODO: add logic to send this event + PROJECT_PLAN_READY: 'notifications.connect.project.planReady', + // When specification of a product is modified PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'notifications.connect.project.productSpecificationModified', }; diff --git a/src/events/busApi.js b/src/events/busApi.js index afb1b432..329559a2 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -1,6 +1,6 @@ import _ from 'lodash'; import 'config'; -import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_MEMBER_ROLE } from '../constants'; +import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE } from '../constants'; import { createEvent } from '../services/busApi'; import models from '../models'; @@ -226,6 +226,7 @@ module.exports = (app, logger) => { logger.debug('receive PROJECT_PHASE_UPDATED event'); const projectId = _.parseInt(req.params.projectId); + const phaseId = _.parseInt(req.params.phaseId); models.Project.findOne({ where: { id: projectId }, @@ -237,6 +238,29 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); + + [ + ['spentBudget', BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT], + ['progress', [BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS, BUS_API_EVENT.PROJECT_PROGRESS_MODIFIED]], + ['details', BUS_API_EVENT.PROJECT_PHASE_UPDATE_SCOPE], + ['status', BUS_API_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE, PROJECT_PHASE_STATUS.ACTIVE], + ['status', BUS_API_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED, PROJECT_PHASE_STATUS.COMPLETED], + ].forEach(([key, events, sendIfEqual]) => { + // eslint-disable-next-line no-param-reassign + events = Array.isArray(events) ? events : [events]; + + // send event(s) only if the target field's value was updated, or when an update matches a "sendIfEqual" value + if ((!sendIfEqual && !_.isEqual(original[key], updated[key])) || + (original[key] !== sendIfEqual && updated[key] === sendIfEqual)) { + events.forEach(event => createEvent(event, { + projectId, + phaseId, + projectName: project.name, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger)); + } + }); }).catch(err => null); // eslint-disable-line no-unused-vars }); From cbfaebf5a280bfc4dd0b721dc6ceb340198b549f Mon Sep 17 00:00:00 2001 From: Gian Franco Zabarino Date: Sun, 15 Jul 2018 03:15:10 -0300 Subject: [PATCH 02/77] Added logic for the plan ready event. --- src/constants.js | 1 - src/events/busApi.js | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/constants.js b/src/constants.js index d0139de2..1414c964 100644 --- a/src/constants.js +++ b/src/constants.js @@ -84,7 +84,6 @@ export const BUS_API_EVENT = { // When product is updated on any field other than specification PROJECT_PLAN_MODIFIED: 'notifications.connect.project.planModified', - // TODO: add logic to send this event PROJECT_PLAN_READY: 'notifications.connect.project.planReady', // When specification of a product is modified diff --git a/src/events/busApi.js b/src/events/busApi.js index 329559a2..e357d68a 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -177,6 +177,34 @@ module.exports = (app, logger) => { }).catch(err => null); // eslint-disable-line no-unused-vars }); + /** + * If the project is in draft status and the phase is in reviewed status, and it's the + * only phase in the project with that status, then send the plan ready event. + * + * @param req the req + * @param project the project + * @param phase the phase that was created/updated + * @returns {Promise} + */ + async function sendPlanReadyEventIfNeeded(req, project, phase) { + if (project.status === PROJECT_STATUS.DRAFT && + phase.status === PROJECT_PHASE_STATUS.REVIEWED) { + await models.ProjectPhase.count({ + where: { projectId: project.id, status: PROJECT_PHASE_STATUS.REVIEWED }, + }).then(((count) => { + // only send the plan ready event when this is the only reviewed phase in the project + if (count !== 1) { return; } + createEvent(BUS_API_EVENT.PROJECT_PLAN_READY, { + projectId: project.id, + phaseId: phase.id, + projectName: project.name, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + })); + } + } + /** * PROJECT_PHASE_ADDED */ @@ -195,6 +223,8 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); + + return sendPlanReadyEventIfNeeded(req, project, created); }).catch(err => null); // eslint-disable-line no-unused-vars }); @@ -248,7 +278,7 @@ module.exports = (app, logger) => { ].forEach(([key, events, sendIfEqual]) => { // eslint-disable-next-line no-param-reassign events = Array.isArray(events) ? events : [events]; - + // send event(s) only if the target field's value was updated, or when an update matches a "sendIfEqual" value if ((!sendIfEqual && !_.isEqual(original[key], updated[key])) || (original[key] !== sendIfEqual && updated[key] === sendIfEqual)) { @@ -261,6 +291,8 @@ module.exports = (app, logger) => { }, logger)); } }); + + return sendPlanReadyEventIfNeeded(req, project, updated); }).catch(err => null); // eslint-disable-line no-unused-vars }); From c1aa6fe713520406ea84763d01465ff44d842202 Mon Sep 17 00:00:00 2001 From: gondzo Date: Thu, 23 Aug 2018 12:44:03 +0200 Subject: [PATCH 03/77] Update constants.js --- src/constants.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/constants.js b/src/constants.js index 1414c964..d517b4b6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -61,11 +61,11 @@ export const BUS_API_EVENT = { PROJECT_CANCELED: 'notifications.connect.project.canceled', PROJECT_ACTIVE: 'notifications.connect.project.active', - PROJECT_PHASE_TRANSITION_ACTIVE: 'project.phase.transition.active', - PROJECT_PHASE_TRANSITION_COMPLETED: 'project.phase.transition.completed', - PROJECT_PHASE_UPDATE_PAYMENT: 'project.phase.update.payment', - PROJECT_PHASE_UPDATE_PROGRESS: 'project.phase.update.progress', - PROJECT_PHASE_UPDATE_SCOPE: 'project.phase.update.scope', + PROJECT_PHASE_TRANSITION_ACTIVE: 'notifications.connect.project.phase.transition.active', + PROJECT_PHASE_TRANSITION_COMPLETED: 'notifications.connect.project.phase.transition.completed', + PROJECT_PHASE_UPDATE_PAYMENT: 'notifications.connect.project.phase.update.payment', + PROJECT_PHASE_UPDATE_PROGRESS: 'notifications.connect.project.phase.update.progress', + PROJECT_PHASE_UPDATE_SCOPE: 'notifications.connect.project.phase.update.scope', MEMBER_JOINED: 'notifications.connect.project.member.joined', MEMBER_LEFT: 'notifications.connect.project.member.left', From 66a33229718288351ac721ff8a1e00648bbbae13 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 24 Aug 2018 14:30:20 +0530 Subject: [PATCH 04/77] add missing field in sql --- migrations/20180608_project_add_templateId_and_new_tables.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations/20180608_project_add_templateId_and_new_tables.sql b/migrations/20180608_project_add_templateId_and_new_tables.sql index 3f3d6723..84f5f25b 100644 --- a/migrations/20180608_project_add_templateId_and_new_tables.sql +++ b/migrations/20180608_project_add_templateId_and_new_tables.sql @@ -45,6 +45,7 @@ CREATE TABLE milestones ( "activeText" character varying(512) NOT NULL, "completedText" character varying(512) NOT NULL, "blockedText" character varying(512) NOT NULL, + "hidden" boolean DEFAULT false, "deletedAt" timestamp with time zone, "createdAt" timestamp with time zone, "updatedAt" timestamp with time zone, From 95085eb810cdda589e6cb37c598884e19c0f718d Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 24 Aug 2018 15:10:34 +0530 Subject: [PATCH 05/77] lint fix --- src/events/busApi.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 73cbc729..0254944d 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -211,10 +211,10 @@ module.exports = (app, logger) => { * If the project is in draft status and the phase is in reviewed status, and it's the * only phase in the project with that status, then send the plan ready event. * - * @param req the req - * @param project the project - * @param phase the phase that was created/updated - * @returns {Promise} + * @param {object} req the req + * @param {object} project the project + * @param {object} phase the phase that was created/updated + * @returns {Promise} void */ async function sendPlanReadyEventIfNeeded(req, project, phase) { if (project.status === PROJECT_STATUS.DRAFT && @@ -223,14 +223,15 @@ module.exports = (app, logger) => { where: { projectId: project.id, status: PROJECT_PHASE_STATUS.REVIEWED }, }).then(((count) => { // only send the plan ready event when this is the only reviewed phase in the project - if (count !== 1) { return; } - createEvent(BUS_API_EVENT.PROJECT_PLAN_READY, { - projectId: project.id, - phaseId: phase.id, - projectName: project.name, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); + if (count === 1) { + createEvent(BUS_API_EVENT.PROJECT_PLAN_READY, { + projectId: project.id, + phaseId: phase.id, + projectName: project.name, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } })); } } From f979c48129b599fa16e23463566b97ffbdc2c969 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 24 Aug 2018 15:11:19 +0530 Subject: [PATCH 06/77] fixed bug in detecting the non product spec change --- src/events/busApi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 0254944d..c10613bc 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -403,7 +403,7 @@ module.exports = (app, logger) => { // Other fields change const originalWithouDetails = _.omit(original, 'details'); const updatedWithouDetails = _.omit(updated, 'details'); - if (!_.isEqual(originalWithouDetails.details, updatedWithouDetails.details)) { + if (!_.isEqual(originalWithouDetails, updatedWithouDetails)) { createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { projectId, projectName: project.name, From b82a463f921955254a31964fac2538607c868197 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Sun, 26 Aug 2018 00:37:41 +0700 Subject: [PATCH 07/77] #151 Add `reference` and `referenceId` fields instead of `productTemplateId` in milestoneTemplates #161 Move all supporting entities endpoints under `/v4/projects/metadata` --- .../20180824_milestone_templates_metadata.sql | 27 ++ postman.json | 338 +++++++++++++----- src/constants.js | 6 + src/middlewares/validateMilestoneTemplate.js | 154 ++++++++ ...estoneTemplate.js => milestoneTemplate.js} | 13 +- src/models/productTemplate.js | 9 - src/routes/index.js | 59 ++- src/routes/milestoneTemplates/clone.js | 96 ++--- src/routes/milestoneTemplates/clone.spec.js | 69 ++-- src/routes/milestoneTemplates/create.js | 34 +- src/routes/milestoneTemplates/create.spec.js | 57 +-- src/routes/milestoneTemplates/delete.js | 35 +- src/routes/milestoneTemplates/delete.spec.js | 99 +++-- src/routes/milestoneTemplates/get.js | 27 +- src/routes/milestoneTemplates/get.spec.js | 41 +-- src/routes/milestoneTemplates/list.js | 18 +- src/routes/milestoneTemplates/list.spec.js | 49 ++- src/routes/milestoneTemplates/update.js | 58 ++- src/routes/milestoneTemplates/update.spec.js | 102 +++--- src/routes/productCategories/create.spec.js | 26 +- src/routes/productCategories/delete.spec.js | 20 +- src/routes/productCategories/get.spec.js | 18 +- src/routes/productCategories/list.spec.js | 14 +- src/routes/productCategories/update.spec.js | 32 +- src/routes/productTemplates/create.spec.js | 20 +- src/routes/productTemplates/delete.spec.js | 20 +- src/routes/productTemplates/get.spec.js | 18 +- src/routes/productTemplates/list.spec.js | 16 +- src/routes/productTemplates/update.spec.js | 20 +- src/routes/projectMembers/delete.js | 4 +- src/routes/projectTemplates/create.spec.js | 20 +- src/routes/projectTemplates/delete.spec.js | 20 +- src/routes/projectTemplates/get.spec.js | 18 +- src/routes/projectTemplates/list.spec.js | 14 +- src/routes/projectTemplates/update.spec.js | 20 +- src/routes/projectTypes/create.spec.js | 28 +- src/routes/projectTypes/delete.spec.js | 20 +- src/routes/projectTypes/get.spec.js | 18 +- src/routes/projectTypes/list.spec.js | 14 +- src/routes/projectTypes/update.spec.js | 34 +- src/routes/timelines/create.js | 8 +- src/routes/timelines/create.spec.js | 14 +- src/tests/seed.js | 18 +- swagger.yaml | 93 +++-- 44 files changed, 1080 insertions(+), 758 deletions(-) create mode 100644 migrations/20180824_milestone_templates_metadata.sql create mode 100644 src/middlewares/validateMilestoneTemplate.js rename src/models/{productMilestoneTemplate.js => milestoneTemplate.js} (77%) diff --git a/migrations/20180824_milestone_templates_metadata.sql b/migrations/20180824_milestone_templates_metadata.sql new file mode 100644 index 00000000..ef04ac62 --- /dev/null +++ b/migrations/20180824_milestone_templates_metadata.sql @@ -0,0 +1,27 @@ +-- +-- UPDATE EXISTING TABLES: +-- product_milestone_templates: +-- removed constraint `product_milestone_templates_productTemplateId_fkey` +-- changed column `productTemplateId` to `referenceId` +-- added column `reference` +-- added column `metadata` +-- changed table name to `milestone_templates` + +-- +-- product_milestone_templates +-- +ALTER TABLE product_milestone_templates DROP CONSTRAINT "product_milestone_templates_productTemplateId_fkey"; + +ALTER TABLE product_milestone_templates RENAME COLUMN "productTemplateId" TO referenceId; + +ALTER TABLE product_milestone_templates ADD COLUMN "reference" character varying(45); +UPDATE product_milestone_templates set reference='product' where reference is null; +ALTER TABLE product_milestone_templates ALTER COLUMN "reference" SET NOT NULL; + +ALTER TABLE product_milestone_templates ADD COLUMN "metadata" json; +UPDATE product_milestone_templates set metadata='{}' where metadata is null; +ALTER TABLE product_milestone_templates ALTER COLUMN "metadata" SET NOT NULL; + +ALTER TABLE product_milestone_templates RENAME TO milestone_templates; + + diff --git a/postman.json b/postman.json index ed8a690f..112660b6 100644 --- a/postman.json +++ b/postman.json @@ -1,8 +1,7 @@ { "info": { + "_postman_id": "6b341e19-8aa0-4827-8bdb-d676c3c5e20b", "name": "tc-project-service", - "_postman_id": "63bb8939-b1c0-0c3c-ad9d-68e63063eda7", - "description": "", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ @@ -371,7 +370,6 @@ }, { "name": "Project With TemplateId issue", - "description": "", "item": [ { "name": "Create project with templateId (not existed)", @@ -821,7 +819,6 @@ }, { "name": "Projects", - "description": "Requests for all things projects.", "item": [ { "name": "Create project without payload", @@ -1732,7 +1729,8 @@ }, "response": [] } - ] + ], + "description": "Requests for all things projects." }, { "name": "EventHandling and Integration with Direct Project API", @@ -2437,12 +2435,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"app\",\r\n \"icon\": \"http://example.com/icon1.ico\",\r\n \"question\": \"question 1\",\r\n \"info\": \"info 1\",\r\n \"aliases\": [\"key-1\", \"key_1\"],\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTemplates", + "raw": "{{api-url}}/v4/projects/metadata/projectTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTemplates" ] } @@ -2468,12 +2468,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTemplates", + "raw": "{{api-url}}/v4/projects/metadata/projectTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTemplates" ] } @@ -2499,12 +2501,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTemplates/1", + "raw": "{{api-url}}/v4/projects/metadata/projectTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTemplates", "1" ] @@ -2531,12 +2535,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"app\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\",\r\n \"scope2\": [\"a\"]\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\",\r\n \"phase2\": {\r\n \t\"another\": \"another\"\r\n }\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTemplates/1", + "raw": "{{api-url}}/v4/projects/metadata/projectTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTemplates", "1" ] @@ -2563,12 +2569,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\",\r\n \"scope2\": [\"a\"]\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\",\r\n \"phase2\": {\r\n \t\"another\": \"another\"\r\n }\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTemplates/2", + "raw": "{{api-url}}/v4/projects/metadata/projectTemplates/2", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTemplates", "2" ] @@ -2600,12 +2608,14 @@ "raw": "{\r\n \"param\": {\r\n \"name\": \"name 1\",\r\n \"productKey\": \"productKey 1\",\r\n \"category\": \"key1\",\r\n \"icon\": \"http://example.com/icon1.ico\",\r\n \"brief\": \"brief 1\",\r\n \"details\": \"details 1\",\r\n \"aliases\": [\"product key 1\", \"product_key_1\"],\r\n \"template\": {\r\n \"template1\": {\r\n \"name\": \"template 1\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 1\"\r\n },\r\n \"others\": [\"others 11\", \"others 12\"]\r\n },\r\n \"template2\": {\r\n \"name\": \"template 2\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 2\"\r\n },\r\n \"others\": [\"others 21\", \"others 22\"]\r\n }\r\n }\r\n }\r\n }" }, "url": { - "raw": "{{api-url}}/v4/productTemplates", + "raw": "{{api-url}}/v4/projects/metadata/productTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productTemplates" ] } @@ -2631,12 +2641,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates", + "raw": "{{api-url}}/v4/projects/metadata/productTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productTemplates" ] } @@ -2662,12 +2674,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/3", + "raw": "{{api-url}}/v4/projects/metadata/productTemplates/3", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productTemplates", "3" ] @@ -2694,12 +2708,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"productKey\":\"new productKey\",\r\n \"category\":\"key1\",\r\n \"icon\":\"http://example.com/icon-new.ico\",\r\n \"brief\": \"new brief\",\r\n \"details\": \"new details\",\r\n \"aliases\":{\r\n \"alias1\":\"scope 1\",\r\n \"alias2\": [\"a\"]\r\n },\r\n \"template\":{\r\n \"template1\":\"template 1\",\r\n \"template2\": {\r\n \t\"another\": \"another\"\r\n }\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1", + "raw": "{{api-url}}/v4/projects/metadata/productTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productTemplates", "1" ] @@ -2726,12 +2742,14 @@ "raw": "" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1", + "raw": "{{api-url}}/v4/projects/metadata/productTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productTemplates", "1" ] @@ -2760,15 +2778,17 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"key\": \"new key\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"http://example.com/icon4.ico\",\r\n \t\"question\": \"question 4\",\r\n \t\"info\": \"info 4\",\r\n \t\"aliases\": [\"key-41\", \"key_42\"]\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"key\": \"new key\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"http://example.com/icon4.ico\",\r\n \t\"question\": \"question 4\",\r\n \t\"info\": \"info 4\",\r\n \t\"aliases\": [\"key-41\", \"key_42\"],\r\n \t\"metadata\": {}\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTypes", + "raw": "{{api-url}}/v4/projects/metadata/projectTypes", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTypes" ] } @@ -2794,12 +2814,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTypes", + "raw": "{{api-url}}/v4/projects/metadata/projectTypes", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTypes" ] } @@ -2825,12 +2847,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTypes/generic", + "raw": "{{api-url}}/v4/projects/metadata/projectTypes/generic", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTypes", "generic" ] @@ -2857,12 +2881,14 @@ "raw": "{\r\n \"param\":{\r\n \"displayName\": \"Chatbot-updated\"\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/projectTypes/chatbot", + "raw": "{{api-url}}/v4/projects/metadata/projectTypes/chatbot", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTypes", "chatbot" ] @@ -2889,12 +2915,14 @@ "raw": "" }, "url": { - "raw": "{{api-url}}/v4/projectTypes/chatbot", + "raw": "{{api-url}}/v4/projects/metadata/projectTypes/chatbot", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "projectTypes", "chatbot" ] @@ -2926,12 +2954,14 @@ "raw": "{\r\n \"param\":{\r\n \"key\": \"generic\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"icon\",\r\n \"question\": \"question\",\r\n \"info\": \"info\",\r\n \"aliases\": [\"key-1\", \"key-2\"]\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productCategories", + "raw": "{{api-url}}/v4/projects/metadata/productCategories", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productCategories" ] } @@ -2957,12 +2987,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productCategories", + "raw": "{{api-url}}/v4/projects/metadata/productCategories", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productCategories" ] } @@ -2988,12 +3020,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productCategories/generic", + "raw": "{{api-url}}/v4/projects/metadata/productCategories/generic", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productCategories", "generic" ] @@ -3020,12 +3054,14 @@ "raw": "{\r\n \"param\":{\r\n \"displayName\": \"Chatbot-updated\"\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productCategories/generic", + "raw": "{{api-url}}/v4/projects/metadata/productCategories/generic", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productCategories", "generic" ] @@ -3052,12 +3088,14 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\",\r\n \"scope2\": [\"a\"]\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\",\r\n \"phase2\": {\r\n \t\"another\": \"another\"\r\n }\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productCategories/generic", + "raw": "{{api-url}}/v4/projects/metadata/productCategories/generic", "host": [ "{{api-url}}" ], "path": [ "v4", + "projects", + "metadata", "productCategories", "generic" ] @@ -3101,7 +3139,6 @@ }, { "name": "Project upgrade", - "description": "Request to migrate projects.", "item": [ { "name": "Migrate project", @@ -3235,7 +3272,8 @@ }, "response": [] } - ] + ], + "description": "Request to migrate projects." }, { "name": "Timeline", @@ -3271,6 +3309,37 @@ }, "response": [] }, + { + "name": "Create timeline with templateId", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token-connectAdmin-40051336}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"description\":\"new description\",\r\n \"startDate\":\"2018-05-29T00:00:00.000Z\",\r\n \"endDate\": \"2018-05-30T00:00:00.000Z\",\r\n \"reference\": \"project\",\r\n \"referenceId\": 1,\r\n \"templateId\": 1\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines" + ] + } + }, + "response": [] + }, { "name": "Create timeline with invalid data", "request": { @@ -3332,8 +3401,7 @@ "query": [ { "key": "filter", - "value": "reference%3Dphase%26referenceId%3D1", - "equals": true + "value": "reference%3Dphase%26referenceId%3D1" } ] } @@ -3902,18 +3970,51 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\"\r\n\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {}\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones" + "timelines", + "metadata", + "milestoneTemplates" + ] + } + }, + "response": [] + }, + { + "name": "Create milestone template with invalid referenceId", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token-admin-40051333}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1000,\r\n\t\"metadata\": {}\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines", + "metadata", + "milestoneTemplates" ] } }, @@ -3938,15 +4039,15 @@ "raw": "{\r\n \"param\":{\r\n\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones" + "timelines", + "metadata", + "milestoneTemplates" ] } }, @@ -3968,18 +4069,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceTemplateId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/2/milestones/clone", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "2", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "clone" ] } @@ -3987,7 +4088,7 @@ "response": [] }, { - "name": "Clone milestone template with invalid product template id", + "name": "Clone milestone template with invalid referenceId", "request": { "method": "POST", "header": [ @@ -4002,18 +4103,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceTemplateId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2000\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/5/milestones/clone", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "5", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "clone" ] } @@ -4021,7 +4122,7 @@ "response": [] }, { - "name": "Clone milestone template with invalid source product template id", + "name": "Clone milestone template with invalid sourceReferenceId", "request": { "method": "POST", "header": [ @@ -4036,18 +4137,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceTemplateId\": 6\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1000,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/2/milestones/clone", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "2", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "clone" ] } @@ -4073,15 +4174,54 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones" + "timelines", + "metadata", + "milestoneTemplates" + ] + } + }, + "response": [] + }, + { + "name": "List milestone templates (filter)", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token-copilot-40051332}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines", + "metadata", + "milestoneTemplates" + ], + "query": [ + { + "key": "filter", + "value": "reference%3Dproduct%26referenceId%3D1" + } ] } }, @@ -4106,17 +4246,21 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones?sort=order desc", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1&sort=order desc", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones" + "timelines", + "metadata", + "milestoneTemplates" ], "query": [ + { + "key": "filter", + "value": "reference%3Dproduct%26referenceId%3D1" + }, { "key": "sort", "value": "order desc" @@ -4145,15 +4289,15 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4176,18 +4320,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4210,18 +4354,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 2\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 2,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4244,18 +4388,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4278,18 +4422,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 3\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 3,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4312,18 +4456,18 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "1" ] } @@ -4349,15 +4493,15 @@ "raw": "" }, "url": { - "raw": "{{api-url}}/v4/productTemplates/1/milestones/2", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/2", "host": [ "{{api-url}}" ], "path": [ "v4", - "productTemplates", - "1", - "milestones", + "timelines", + "metadata", + "milestoneTemplates", "2" ] } diff --git a/src/constants.js b/src/constants.js index 12696193..bfcef360 100644 --- a/src/constants.js +++ b/src/constants.js @@ -104,3 +104,9 @@ export const TIMELINE_REFERENCES = { PHASE: 'phase', PRODUCT: 'product', }; + +export const MILESTONE_TEMPLATE_REFERENCES = { + PROJECT: 'project', + PRODUCT: 'product', +}; + diff --git a/src/middlewares/validateMilestoneTemplate.js b/src/middlewares/validateMilestoneTemplate.js new file mode 100644 index 00000000..43c59c38 --- /dev/null +++ b/src/middlewares/validateMilestoneTemplate.js @@ -0,0 +1,154 @@ +import _ from 'lodash'; +import { MILESTONE_TEMPLATE_REFERENCES } from '../constants'; +import models from '../models'; +import util from '../util'; + +// eslint-disable-next-line valid-jsdoc +/** + * Common validation code for types of milestone template references. + * @param {{ reference: string, referenceId: string|number }} sourceObject + * @returns {Promise} + */ +async function validateReference(sourceObject) { + // The source object refers to a project template + if (sourceObject.reference === MILESTONE_TEMPLATE_REFERENCES.PROJECT) { + // Validate projectTemplateId to be existed + const projectTemplate = await models.ProjectTemplate.findOne({ + where: { + id: sourceObject.referenceId, + deletedAt: { $eq: null }, + }, + }); + if (!projectTemplate) { + const apiErr = new Error( + `Project template not found for project id ${sourceObject.referenceId}`); + apiErr.status = 422; + throw apiErr; + } + return; + } + + // The source object refers to a product template + if (sourceObject.reference === MILESTONE_TEMPLATE_REFERENCES.PRODUCT) { + // Validate ProductTemplate to be existed + const productTemplate = await models.ProductTemplate.findOne({ + where: { + id: sourceObject.referenceId, + deletedAt: { $eq: null }, + }, + }); + if (!productTemplate) { + const apiErr = new Error( + `Product template not found for product id ${sourceObject.referenceId}`); + apiErr.status = 422; + throw apiErr; + } + } +} + +const validateMilestoneTemplate = { + + /** + * The middleware to validate MilestoneTemplate request object. + * This should be called after the validate() middleware, + * and before the permissions() middleware. + * @param {Object} req the express request instance + * @param {Object} res the express response instance + * @param {Function} next the express next middleware + */ + // eslint-disable-next-line valid-jsdoc + validateRequestBody: (req, res, next) => { + validateReference(req.body.param, req) + .then(() => { + if (req.body.param.sourceReference) { + return validateReference({ + reference: req.body.param.sourceReference, + referenceId: req.body.param.sourceReferenceId, + }); + } + + return Promise.resolve(); + }) + .then(next) + .catch(next); + }, + + /** + * The middleware to validate reference/referenceId pair + * present in the request's query filter and set to the request params. Because of the filter needs + * to be parsed, this can be the first middleware in the stack, and can be placed before the permissions() + * middleware. + * @param {Object} req the express request instance + * @param {Object} res the express response instance + * @param {Function} next the express next middleware + */ + // eslint-disable-next-line valid-jsdoc + validateQueryFilter: (req, res, next) => { + if (!req.query.filter) { + return next(); + } + + // Validate the filter + const filter = util.parseQueryFilter(req.query.filter); + + // Save the parsed filter for later + req.params.filter = filter; + + if (!util.isValidFilter(filter, ['reference', 'referenceId'])) { + const apiErr = new Error('Only allowed to filter by reference and referenceId'); + apiErr.status = 422; + return next(apiErr); + } + + // Verify required filters are present + if (!filter.reference || !filter.referenceId) { + const apiErr = new Error('Please provide reference and referenceId filter parameters'); + apiErr.status = 422; + return next(apiErr); + } + + // Verify reference is a valid value + if (!_.includes(MILESTONE_TEMPLATE_REFERENCES, filter.reference)) { + const apiErr = new Error(`reference filter must be in ${MILESTONE_TEMPLATE_REFERENCES}`); + apiErr.status = 422; + return next(apiErr); + } + + if (_.lt(filter.referenceId, 1)) { + const apiErr = new Error('referenceId filter must be a positive integer'); + apiErr.status = 422; + return next(apiErr); + } + + return validateReference(filter, req) + .then(next) + .catch(next); + }, + + /** + * The middleware to validate milestoneTemplateId from request + * path parameter, and set to the request params. This should be called after the validate() + * middleware, and before the permissions() middleware. + * @param {Object} req the express request instance + * @param {Object} res the express response instance + * @param {Function} next the express next middleware + */ + // eslint-disable-next-line valid-jsdoc + validateIdParam: (req, res, next) => { + models.MilestoneTemplate.findById(req.params.milestoneTemplateId) + .then((milestoneTemplate) => { + if (!milestoneTemplate) { + const apiErr = new Error( + `MilestoneTemplate not found for id ${req.params.milestoneTemplateId}`); + apiErr.status = 404; + return next(apiErr); + } + + req.milestoneTemplate = milestoneTemplate; + + return next(); + }); + }, +}; + +export default validateMilestoneTemplate; diff --git a/src/models/productMilestoneTemplate.js b/src/models/milestoneTemplate.js similarity index 77% rename from src/models/productMilestoneTemplate.js rename to src/models/milestoneTemplate.js index 7db76b52..feb37a1d 100644 --- a/src/models/productMilestoneTemplate.js +++ b/src/models/milestoneTemplate.js @@ -1,10 +1,10 @@ /* eslint-disable valid-jsdoc */ /** - * The Product Milestone Template model + * The Milestone Template model */ module.exports = (sequelize, DataTypes) => { - const ProductMilestoneTemplate = sequelize.define('ProductMilestoneTemplate', { + const MilestoneTemplate = sequelize.define('MilestoneTemplate', { id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.STRING(255), allowNull: false }, description: DataTypes.STRING(255), @@ -16,6 +16,11 @@ module.exports = (sequelize, DataTypes) => { completedText: { type: DataTypes.STRING(512), allowNull: false }, blockedText: { type: DataTypes.STRING(512), allowNull: false }, hidden: { type: DataTypes.BOOLEAN, defaultValue: false }, + + reference: { type: DataTypes.STRING(45), allowNull: false }, + referenceId: { type: DataTypes.BIGINT, allowNull: false }, + metadata: { type: DataTypes.JSON, defaultValue: {}, allowNull: false }, + deletedAt: DataTypes.DATE, createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, @@ -23,7 +28,7 @@ module.exports = (sequelize, DataTypes) => { createdBy: { type: DataTypes.BIGINT, allowNull: false }, updatedBy: { type: DataTypes.BIGINT, allowNull: false }, }, { - tableName: 'product_milestone_templates', + tableName: 'milestone_templates', paranoid: true, timestamps: true, updatedAt: 'updatedAt', @@ -31,5 +36,5 @@ module.exports = (sequelize, DataTypes) => { deletedAt: 'deletedAt', }); - return ProductMilestoneTemplate; + return MilestoneTemplate; }; diff --git a/src/models/productTemplate.js b/src/models/productTemplate.js index fe955de8..4e4dc184 100644 --- a/src/models/productTemplate.js +++ b/src/models/productTemplate.js @@ -29,15 +29,6 @@ module.exports = (sequelize, DataTypes) => { updatedAt: 'updatedAt', createdAt: 'createdAt', deletedAt: 'deletedAt', - classMethods: { - associate: (models) => { - ProductTemplate.hasMany(models.ProductMilestoneTemplate, { - as: 'milestones', - foreignKey: 'productTemplateId', - onDelete: 'cascade', - }); - }, - }, }); return ProductTemplate; diff --git a/src/routes/index.js b/src/routes/index.js index 8e4f034f..5b7a053d 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -26,29 +26,28 @@ router.get(`/${apiVersion}/projects/health`, (req, res) => { // All project service endpoints need authentication const jwtAuth = require('tc-core-library-js').middleware.jwtAuthenticator; -router.route('/v4/projectTemplates') +router.route('/v4/projects/metadata/projectTemplates') .get(require('./projectTemplates/list')); -router.route('/v4/projectTemplates/:templateId(\\d+)') +router.route('/v4/projects/metadata/projectTemplates/:templateId(\\d+)') .get(require('./projectTemplates/get')); -router.route('/v4/productTemplates') +router.route('/v4/projects/metadata/productTemplates') .get(require('./productTemplates/list')); -router.route('/v4/productTemplates/:templateId(\\d+)') +router.route('/v4/projects/metadata/productTemplates/:templateId(\\d+)') .get(require('./productTemplates/get')); -router.route('/v4/projectTypes') +router.route('/v4/projects/metadata/projectTypes') .get(require('./projectTypes/list')); -router.route('/v4/projectTypes/:key') +router.route('/v4/projects/metadata/projectTypes/:key') .get(require('./projectTypes/get')); -router.route('/v4/productCategories') +router.route('/v4/projects/metadata/productCategories') .get(require('./productCategories/list')); -router.route('/v4/productCategories/:key') +router.route('/v4/projects/metadata/productCategories/:key') .get(require('./productCategories/get')); router.all( - RegExp(`\\/${apiVersion}\\/(projects|projectTemplates|productTemplates|productCategories|projectTypes|` + - 'timelines)(?!\\/health).*'), jwtAuth()); + RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), jwtAuth()); // Register all the routes router.route('/v4/projects') @@ -90,32 +89,20 @@ router.route('/v4/projects/:projectId(\\d+)/attachments/:id(\\d+)') router.route('/v4/projects/:projectId(\\d+)/upgrade') .post(require('./projectUpgrade/create')); -router.route('/v4/projectTemplates') +router.route('/v4/projects/metadata/projectTemplates') .post(require('./projectTemplates/create')); -router.route('/v4/projectTemplates/:templateId(\\d+)') +router.route('/v4/projects/metadata/projectTemplates/:templateId(\\d+)') .patch(require('./projectTemplates/update')) .delete(require('./projectTemplates/delete')); -router.route('/v4/productTemplates') +router.route('/v4/projects/metadata/productTemplates') .post(require('./productTemplates/create')); -router.route('/v4/productTemplates/:templateId(\\d+)') +router.route('/v4/projects/metadata/productTemplates/:templateId(\\d+)') .patch(require('./productTemplates/update')) .delete(require('./productTemplates/delete')); -router.route('/v4/productTemplates/:productTemplateId(\\d+)/milestones') - .post(require('./milestoneTemplates/create')) - .get(require('./milestoneTemplates/list')); - -router.route('/v4/productTemplates/:productTemplateId(\\d+)/milestones/clone') - .post(require('./milestoneTemplates/clone')); - -router.route('/v4/productTemplates/:productTemplateId(\\d+)/milestones/:milestoneTemplateId(\\d+)') - .get(require('./milestoneTemplates/get')) - .patch(require('./milestoneTemplates/update')) - .delete(require('./milestoneTemplates/delete')); - router.route('/v4/projects/:projectId(\\d+)/phases') .get(require('./phases/list')) .post(require('./phases/create')); @@ -134,17 +121,17 @@ router.route('/v4/projects/:projectId(\\d+)/phases/:phaseId(\\d+)/products/:prod .patch(require('./phaseProducts/update')) .delete(require('./phaseProducts/delete')); -router.route('/v4/productCategories') +router.route('/v4/projects/metadata/productCategories') .post(require('./productCategories/create')); -router.route('/v4/productCategories/:key') +router.route('/v4/projects/metadata/productCategories/:key') .patch(require('./productCategories/update')) .delete(require('./productCategories/delete')); -router.route('/v4/projectTypes') +router.route('/v4/projects/metadata/projectTypes') .post(require('./projectTypes/create')); -router.route('/v4/projectTypes/:key') +router.route('/v4/projects/metadata/projectTypes/:key') .patch(require('./projectTypes/update')) .delete(require('./projectTypes/delete')); @@ -166,6 +153,18 @@ router.route('/v4/timelines/:timelineId(\\d+)/milestones/:milestoneId(\\d+)') .patch(require('./milestones/update')) .delete(require('./milestones/delete')); +router.route('/v4/timelines/metadata/milestoneTemplates') + .post(require('./milestoneTemplates/create')) + .get(require('./milestoneTemplates/list')); + +router.route('/v4/timelines/metadata/milestoneTemplates/clone') + .post(require('./milestoneTemplates/clone')); + +router.route('/v4/timelines/metadata/milestoneTemplates/:milestoneTemplateId(\\d+)') + .get(require('./milestoneTemplates/get')) + .patch(require('./milestoneTemplates/update')) + .delete(require('./milestoneTemplates/delete')); + // register error handler router.use((err, req, res, next) => { // eslint-disable-line no-unused-vars // DO NOT REMOVE next arg.. even though eslint diff --git a/src/routes/milestoneTemplates/clone.js b/src/routes/milestoneTemplates/clone.js index c7fd1bf3..bfe95a46 100644 --- a/src/routes/milestoneTemplates/clone.js +++ b/src/routes/milestoneTemplates/clone.js @@ -7,94 +7,68 @@ import Joi from 'joi'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import models from '../../models'; +import { MILESTONE_TEMPLATE_REFERENCES } from '../../constants'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; const permissions = tcMiddleware.permissions; const schema = { - params: { - productTemplateId: Joi.number().integer().positive().required(), - }, body: { param: Joi.object().keys({ - sourceTemplateId: Joi.number().integer().positive().required(), + sourceReference: Joi.string().valid(_.values(MILESTONE_TEMPLATE_REFERENCES)).required(), + sourceReferenceId: Joi.number().integer().positive().required(), + reference: Joi.string().valid(_.values(MILESTONE_TEMPLATE_REFERENCES)).required(), + referenceId: Joi.number().integer().positive().required(), }).required(), }, }; module.exports = [ validate(schema), + validateMilestoneTemplate.validateRequestBody, permissions('milestoneTemplate.clone'), (req, res, next) => { let result; return models.sequelize.transaction(tx => // Find the product template - models.ProductTemplate.findAll({ where: { id: [req.params.productTemplateId, req.body.param.sourceTemplateId] }, - transaction: tx }) - .then((productTemplates) => { - // Not found - if (!productTemplates) { - const apiErr = new Error( - `Product template not found for product template ids ${req.params.productTemplateId} - ${req.body.param.sourceTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - const targetProductTemplate = _.find(productTemplates, ['id', req.params.productTemplateId]); - const sourceProductTemplate = _.find(productTemplates, ['id', req.body.param.sourceTemplateId]); - - // Not found - if (!targetProductTemplate) { - const apiErr = new Error( - `Product template not found for product template id ${req.params.productTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - // Not found - if (!sourceProductTemplate) { - const apiErr = new Error( - `Product template not found for source product template id ${req.body.param.sourceTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - return models.ProductMilestoneTemplate.findAll({ - where: { - productTemplateId: req.body.param.sourceTemplateId, - }, - attributes: { exclude: ['id', 'deletedAt', 'createdAt', 'updatedAt', 'deletedBy'] }, - raw: true, - }) - .then((milestoneTemplatesToClone) => { - const newMilestoneTemplates = _.cloneDeep(milestoneTemplatesToClone); - _.each(newMilestoneTemplates, (milestone) => { - milestone.productTemplateId = req.params.productTemplateId; // eslint-disable-line no-param-reassign - milestone.createdBy = req.authUser.userId; // eslint-disable-line no-param-reassign - milestone.updatedBy = req.authUser.userId; // eslint-disable-line no-param-reassign - }); - return models.ProductMilestoneTemplate.bulkCreate(newMilestoneTemplates, { transaction: tx }); + models.MilestoneTemplate.findAll({ + where: { + reference: req.body.param.sourceReference, + referenceId: req.body.param.sourceReferenceId, + }, + attributes: { exclude: ['id', 'deletedAt', 'createdAt', 'updatedAt', 'deletedBy'] }, + raw: true, + }) + .then((milestoneTemplatesToClone) => { + const newMilestoneTemplates = _.cloneDeep(milestoneTemplatesToClone); + _.each(newMilestoneTemplates, (milestone) => { + milestone.reference = req.body.param.reference; // eslint-disable-line no-param-reassign + milestone.referenceId = req.body.param.referenceId; // eslint-disable-line no-param-reassign + milestone.createdBy = req.authUser.userId; // eslint-disable-line no-param-reassign + milestone.updatedBy = req.authUser.userId; // eslint-disable-line no-param-reassign }); + return models.MilestoneTemplate.bulkCreate(newMilestoneTemplates, { transaction: tx }); }) .then(() => { // eslint-disable-line arrow-body-style - return models.ProductMilestoneTemplate.findAll({ + return models.MilestoneTemplate.findAll({ where: { - productTemplateId: req.params.productTemplateId, + reference: req.body.param.reference, + referenceId: req.body.param.referenceId, }, attributes: { exclude: ['deletedAt', 'deletedBy'] }, raw: true, }) - .then((clonedMilestoneTemplates) => { - result = clonedMilestoneTemplates; - return result; - }); + .then((clonedMilestoneTemplates) => { + result = clonedMilestoneTemplates; + return result; + }); }), ) - .then(() => { - // Write to response - res.status(201).json(util.wrapResponse(req.id, result, 1, 201)); - }) - .catch(next); + .then(() => { + // Write to response + res.status(201).json(util.wrapResponse(req.id, result, result.length, 201)); + }) + .catch(next); }, ]; diff --git a/src/routes/milestoneTemplates/clone.spec.js b/src/routes/milestoneTemplates/clone.spec.js index 2f008e8d..edd0ec9e 100644 --- a/src/routes/milestoneTemplates/clone.spec.js +++ b/src/routes/milestoneTemplates/clone.spec.js @@ -83,7 +83,9 @@ const milestoneTemplates = [ duration: 3, type: 'type1', order: 1, - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, plannedText: 'text to be shown in planned stage', blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', @@ -100,7 +102,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, }, @@ -109,27 +113,30 @@ const milestoneTemplates = [ describe('CLONE milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('POST /productTemplates/{productTemplateId}/milestones/clone', () => { + describe('POST /timelines/metadata/milestoneTemplates/clone', () => { const body = { param: { - sourceTemplateId: 1, + sourceReference: 'product', + sourceReferenceId: 1, + reference: 'product', + referenceId: 2, }, }; it('should return 403 if user is not authenticated/clone', (done) => { request(server) - .post('/v4/productTemplates/2/milestones') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -139,7 +146,7 @@ describe('CLONE milestone template', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -149,7 +156,7 @@ describe('CLONE milestone template', () => { it('should return 403 for manager', (done) => { request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -157,41 +164,55 @@ describe('CLONE milestone template', () => { .expect(403, done); }); - it('should return 404 for non-existent product template', (done) => { + it('should return 422 for non-existent product template', (done) => { + const invalidBody = { + param: { + sourceReference: 'product', + sourceReferenceId: 1, + reference: 'product', + referenceId: 2000, + }, + }; + request(server) - .post('/v4/productTemplates/1000/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) - .send(body) - .expect(404, done); + .send(invalidBody) + .expect(422, done); }); - it('should return 404 for non-existent source product template', (done) => { + it('should return 422 for non-existent source product template', (done) => { const invalidBody = { param: { - sourceTemplateId: 99, + sourceReference: 'product', + sourceReferenceId: 1000, + reference: 'product', + referenceId: 2, }, }; request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .send(invalidBody) - .expect(404, done); + .expect(422, done); }); - it('should return 422 if missing sourceTemplateId', (done) => { + it('should return 422 if missing sourceReference', (done) => { const invalidBody = { param: { - sourceTemplateId: undefined, + sourceReferenceId: 1000, + reference: 'product', + referenceId: 2, }, }; request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -202,7 +223,7 @@ describe('CLONE milestone template', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -220,7 +241,9 @@ describe('CLONE milestone template', () => { resJson[0].blockedText.should.be.eql(milestoneTemplates[0].blockedText); resJson[0].activeText.should.be.eql(milestoneTemplates[0].activeText); resJson[0].completedText.should.be.eql(milestoneTemplates[0].completedText); - resJson[0].productTemplateId.should.be.eql(2); + resJson[0].reference.should.be.eql('product'); + resJson[0].referenceId.should.be.eql(2); + resJson[0].metadata.should.be.eql({}); resJson[0].createdBy.should.be.eql(40051333); // admin should.exist(resJson[0].createdAt); @@ -235,7 +258,7 @@ describe('CLONE milestone template', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/productTemplates/2/milestones/clone') + .post('/v4/timelines/metadata/milestoneTemplates/clone') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/milestoneTemplates/create.js b/src/routes/milestoneTemplates/create.js index b207b72a..321c9b65 100644 --- a/src/routes/milestoneTemplates/create.js +++ b/src/routes/milestoneTemplates/create.js @@ -8,13 +8,12 @@ import Sequelize from 'sequelize'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import models from '../../models'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; +import { MILESTONE_TEMPLATE_REFERENCES } from '../../constants'; const permissions = tcMiddleware.permissions; const schema = { - params: { - productTemplateId: Joi.number().integer().positive().required(), - }, body: { param: Joi.object().keys({ id: Joi.any().strip(), @@ -27,7 +26,9 @@ const schema = { activeText: Joi.string().max(512).required(), completedText: Joi.string().max(512).required(), blockedText: Joi.string().max(512).required(), - productTemplateId: Joi.any().strip(), + reference: Joi.string().valid(_.values(MILESTONE_TEMPLATE_REFERENCES)).required(), + referenceId: Joi.number().integer().positive().required(), + metadata: Joi.object().required(), hidden: Joi.boolean().optional(), createdAt: Joi.any().strip(), updatedAt: Joi.any().strip(), @@ -41,39 +42,28 @@ const schema = { module.exports = [ validate(schema), + validateMilestoneTemplate.validateRequestBody, permissions('milestoneTemplate.create'), (req, res, next) => { const entity = _.assign(req.body.param, { createdBy: req.authUser.userId, updatedBy: req.authUser.userId, - productTemplateId: req.params.productTemplateId, }); let result; return models.sequelize.transaction(tx => - // Find the product template - models.ProductTemplate.findById(req.params.productTemplateId, { transaction: tx }) - .then((productTemplate) => { - // Not found - if (!productTemplate) { - const apiErr = new Error( - `Product template not found for product template id ${req.params.productTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - // Create the milestone template - return models.ProductMilestoneTemplate.create(entity, { transaction: tx }); - }) + // Create the milestone template + models.MilestoneTemplate.create(entity, { transaction: tx }) .then((createdEntity) => { // Omit deletedAt and deletedBy result = _.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy'); - // Increase the order of the other milestone templates in the same product template, + // Increase the order of the other milestone templates in the same referenceId, // which have `order` >= this milestone template order - return models.ProductMilestoneTemplate.update({ order: Sequelize.literal('"order" + 1') }, { + return models.MilestoneTemplate.update({ order: Sequelize.literal('"order" + 1') }, { where: { - productTemplateId: req.params.productTemplateId, + reference: result.reference, + referenceId: result.referenceId, id: { $ne: result.id }, order: { $gte: result.order }, }, diff --git a/src/routes/milestoneTemplates/create.spec.js b/src/routes/milestoneTemplates/create.spec.js index c821a5fe..ce9cd160 100644 --- a/src/routes/milestoneTemplates/create.spec.js +++ b/src/routes/milestoneTemplates/create.spec.js @@ -64,7 +64,9 @@ const milestoneTemplates = [ duration: 3, type: 'type1', order: 1, - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, plannedText: 'text to be shown in planned stage', blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', @@ -81,7 +83,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, }, @@ -90,11 +94,11 @@ const milestoneTemplates = [ describe('CREATE milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('POST /productTemplates/{productTemplateId}/milestones', () => { + describe('POST /timelines/metadata/milestoneTemplates', () => { const body = { param: { name: 'milestoneTemplate 3', @@ -107,19 +111,22 @@ describe('CREATE milestone template', () => { activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', hidden: true, + reference: 'product', + referenceId: 1, + metadata: {}, }, }; it('should return 403 if user is not authenticated', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -129,7 +136,7 @@ describe('CREATE milestone template', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -139,7 +146,7 @@ describe('CREATE milestone template', () => { it('should return 403 for manager', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -147,14 +154,18 @@ describe('CREATE milestone template', () => { .expect(403, done); }); - it('should return 404 for non-existed product template', (done) => { + it('should return 422 for non-existed product template', (done) => { + const invalidBody = { + param: _.assign({}, body.param, { referenceId: 1000 }), + }; + request(server) - .post('/v4/productTemplates/1000/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) - .send(body) - .expect(404, done); + .send(invalidBody) + .expect(422, done); }); it('should return 422 if missing name', (done) => { @@ -165,7 +176,7 @@ describe('CREATE milestone template', () => { }; request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -182,7 +193,7 @@ describe('CREATE milestone template', () => { }; request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -199,7 +210,7 @@ describe('CREATE milestone template', () => { }; request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -216,7 +227,7 @@ describe('CREATE milestone template', () => { }; request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -227,7 +238,7 @@ describe('CREATE milestone template', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -246,6 +257,9 @@ describe('CREATE milestone template', () => { resJson.blockedText.should.be.eql(body.param.blockedText); resJson.activeText.should.be.eql(body.param.activeText); resJson.completedText.should.be.eql(body.param.completedText); + resJson.reference.should.be.eql(body.param.reference); + resJson.referenceId.should.be.eql(body.param.referenceId); + resJson.metadata.should.be.eql(body.param.metadata); resJson.createdBy.should.be.eql(40051333); // admin should.exist(resJson.createdAt); @@ -255,9 +269,10 @@ describe('CREATE milestone template', () => { should.not.exist(resJson.deletedAt); // Verify 'order' of the other milestones - models.ProductMilestoneTemplate.findAll({ + models.MilestoneTemplate.findAll({ where: { - productTemplateId: 1, + reference: body.param.reference, + referenceId: body.param.referenceId, }, }).then((milestones) => { _.each(milestones, (milestone) => { @@ -278,7 +293,7 @@ describe('CREATE milestone template', () => { const minimalBody = _.cloneDeep(body); delete minimalBody.param.hidden; request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -294,7 +309,7 @@ describe('CREATE milestone template', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/productTemplates/1/milestones') + .post('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/milestoneTemplates/delete.js b/src/routes/milestoneTemplates/delete.js index 43d3fd57..5cd0454b 100644 --- a/src/routes/milestoneTemplates/delete.js +++ b/src/routes/milestoneTemplates/delete.js @@ -5,44 +5,27 @@ import validate from 'express-validation'; import Joi from 'joi'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import models from '../../models'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; const permissions = tcMiddleware.permissions; const schema = { params: { - productTemplateId: Joi.number().integer().positive().required(), milestoneTemplateId: Joi.number().integer().positive().required(), }, }; module.exports = [ validate(schema), + validateMilestoneTemplate.validateIdParam, permissions('milestoneTemplate.delete'), - (req, res, next) => { - const where = { - id: req.params.milestoneTemplateId, - deletedAt: { $eq: null }, - productTemplateId: req.params.productTemplateId, - }; - - return models.sequelize.transaction(() => + (req, res, next) => models.sequelize.transaction(() => // soft delete the record - models.ProductMilestoneTemplate.findOne({ - where, - }).then((existing) => { - if (!existing) { - // handle 404 - const err = new Error( - `Milestone template not found for milestone template id ${req.params.milestoneTemplateId}`); - err.status = 404; - return Promise.reject(err); - } - return existing.update({ deletedBy: req.authUser.userId }); + req.milestoneTemplate.update({ deletedBy: req.authUser.userId }) + .then(entity => entity.destroy()), + ) + .then(() => { + res.status(204).end(); }) - .then(entity => entity.destroy())) - .then(() => { - res.status(204).end(); - }) - .catch(next); - }, + .catch(next), ]; diff --git a/src/routes/milestoneTemplates/delete.spec.js b/src/routes/milestoneTemplates/delete.spec.js index 63091b36..c47db4dd 100644 --- a/src/routes/milestoneTemplates/delete.spec.js +++ b/src/routes/milestoneTemplates/delete.spec.js @@ -8,31 +8,32 @@ import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; -const expectAfterDelete = (productTemplateId, id, err, next) => { +const expectAfterDelete = (id, err, next) => { if (err) throw err; - setTimeout(() => - models.ProductMilestoneTemplate.findOne({ - where: { - id, - productTemplateId, - }, - paranoid: false, - }) - .then((res) => { - if (!res) { - throw new Error('Should found the entity'); - } else { - chai.assert.isNotNull(res.deletedAt); - chai.assert.isNotNull(res.deletedBy); + setTimeout(() => { + models.MilestoneTemplate.findOne({ + where: { + id, + }, + paranoid: false, + }) + .then((res) => { + server.logger.error(`res = ${res}`); + if (!res) { + throw new Error('Should found the entity'); + } else { + chai.assert.isNotNull(res.deletedAt); + chai.assert.isNotNull(res.deletedBy); - request(server) - .get(`/v4/productTemplates/${productTemplateId}/milestones/${id}`) - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, next); - } - }), 500); + request(server) + .get(`/v4/timelines/metadata/milestoneTemplates/${id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(404, next); + } + }); + }, 500); }; const productTemplates = [ { @@ -93,7 +94,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 1, updatedBy: 2, }, @@ -107,7 +110,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, deletedAt: new Date(), @@ -117,20 +122,20 @@ const milestoneTemplates = [ describe('DELETE milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('DELETE /productTemplates/{productTemplateId}/milestones/{milestoneTemplateId}', () => { + describe('DELETE /timelines/metadata/milestoneTemplates/{milestoneTemplateId}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -139,7 +144,7 @@ describe('DELETE milestone template', () => { it('should return 403 for copilot', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -148,25 +153,16 @@ describe('DELETE milestone template', () => { it('should return 403 for manager', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) .expect(403, done); }); - it('should return 404 for non-existed product template', (done) => { - request(server) - .delete('/v4/productTemplates/1234/milestones/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); - }); - it('should return 404 for non-existed milestone template', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/444') + .delete('/v4/timelines/metadata/milestoneTemplates/444') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -175,25 +171,16 @@ describe('DELETE milestone template', () => { it('should return 404 for deleted milestone template', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/2') + .delete('/v4/timelines/metadata/milestoneTemplates/2') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect(404, done); }); - it('should return 422 for invalid productTemplateId param', (done) => { - request(server) - .delete('/v4/productTemplates/0/milestones/2') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(422, done); - }); - it('should return 422 for invalid milestoneTemplateId param', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/0') + .delete('/v4/timelines/metadata/milestoneTemplates/0') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -202,22 +189,22 @@ describe('DELETE milestone template', () => { it('should return 204, for admin, if template was successfully removed', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) .expect(204) - .end(err => expectAfterDelete(1, 1, err, done)); + .end(err => expectAfterDelete(1, err, done)); }); it('should return 204, for connect admin, if template was successfully removed', (done) => { request(server) - .delete('/v4/productTemplates/1/milestones/1') + .delete('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) .expect(204) - .end(err => expectAfterDelete(1, 1, err, done)); + .end(err => expectAfterDelete(1, err, done)); }); }); }); diff --git a/src/routes/milestoneTemplates/get.js b/src/routes/milestoneTemplates/get.js index 1c1fa3f0..1f6cd2f7 100644 --- a/src/routes/milestoneTemplates/get.js +++ b/src/routes/milestoneTemplates/get.js @@ -3,41 +3,22 @@ */ import validate from 'express-validation'; import Joi from 'joi'; +import _ from 'lodash'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; -import models from '../../models'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; const permissions = tcMiddleware.permissions; const schema = { params: { - productTemplateId: Joi.number().integer().positive().required(), milestoneTemplateId: Joi.number().integer().positive().required(), }, }; module.exports = [ validate(schema), + validateMilestoneTemplate.validateIdParam, permissions('milestoneTemplate.view'), - (req, res, next) => models.ProductMilestoneTemplate.findOne({ - where: { - id: req.params.milestoneTemplateId, - productTemplateId: req.params.productTemplateId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - raw: true, - }) - .then((milestoneTemplate) => { - // Not found - if (!milestoneTemplate) { - const apiErr = new Error( - `Milestone template not found for milestone template id ${req.params.milestoneTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - res.json(util.wrapResponse(req.id, milestoneTemplate)); - return Promise.resolve(); - }) - .catch(next), + (req, res) => res.json(util.wrapResponse(req.id, _.omit(req.milestoneTemplate.toJSON(), 'deletedAt', 'deletedBy'))), ]; diff --git a/src/routes/milestoneTemplates/get.spec.js b/src/routes/milestoneTemplates/get.spec.js index 958a30a6..39244fe4 100644 --- a/src/routes/milestoneTemplates/get.spec.js +++ b/src/routes/milestoneTemplates/get.spec.js @@ -69,7 +69,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 1, updatedBy: 2, }, @@ -83,7 +85,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, deletedAt: new Date(), @@ -93,29 +97,20 @@ const milestoneTemplates = [ describe('GET milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('GET /productTemplates/{productTemplateId}/milestones/{milestoneTemplateId}', () => { + describe('GET /timelines/metadata/milestoneTemplates/{milestoneTemplateId}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .expect(403, done); }); - it('should return 404 for non-existed product template', (done) => { - request(server) - .get('/v4/productTemplates/1234/milestones/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(404, done); - }); - it('should return 404 for non-existed milestone template', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1111') + .get('/v4/timelines/metadata/milestoneTemplates/1111') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -124,7 +119,7 @@ describe('GET milestone template', () => { it('should return 404 for deleted milestone template', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/2') + .get('/v4/timelines/metadata/milestoneTemplates/2') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -133,7 +128,7 @@ describe('GET milestone template', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -149,7 +144,9 @@ describe('GET milestone template', () => { resJson.blockedText.should.be.eql(milestoneTemplates[0].blockedText); resJson.activeText.should.be.eql(milestoneTemplates[0].activeText); resJson.completedText.should.be.eql(milestoneTemplates[0].completedText); - resJson.productTemplateId.should.be.eql(milestoneTemplates[0].productTemplateId); + resJson.reference.should.be.eql(milestoneTemplates[0].reference); + resJson.referenceId.should.be.eql(milestoneTemplates[0].referenceId); + resJson.metadata.should.be.eql(milestoneTemplates[0].metadata); resJson.createdBy.should.be.eql(milestoneTemplates[0].createdBy); should.exist(resJson.createdAt); @@ -164,7 +161,7 @@ describe('GET milestone template', () => { it('should return 200 for connect admin', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -174,7 +171,7 @@ describe('GET milestone template', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -184,7 +181,7 @@ describe('GET milestone template', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -193,7 +190,7 @@ describe('GET milestone template', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/productTemplates/1/milestones/1') + .get('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/milestoneTemplates/list.js b/src/routes/milestoneTemplates/list.js index 40b6ae19..64d93acf 100644 --- a/src/routes/milestoneTemplates/list.js +++ b/src/routes/milestoneTemplates/list.js @@ -1,23 +1,16 @@ /** * API to list all milestone templates */ -import validate from 'express-validation'; -import Joi from 'joi'; import _ from 'lodash'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import models from '../../models'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; const permissions = tcMiddleware.permissions; -const schema = { - params: { - productTemplateId: Joi.number().integer().positive().required(), - }, -}; - module.exports = [ - validate(schema), + validateMilestoneTemplate.validateQueryFilter, permissions('milestoneTemplate.view'), (req, res, next) => { // Parse the sort query @@ -36,10 +29,9 @@ module.exports = [ const sortColumnAndOrder = sort.split(' '); // Get all milestone templates - return models.ProductMilestoneTemplate.findAll({ - where: { - productTemplateId: req.params.productTemplateId, - }, + const where = req.params.filter || {}; + return models.MilestoneTemplate.findAll({ + where, order: [sortColumnAndOrder], attributes: { exclude: ['deletedAt', 'deletedBy'] }, raw: true, diff --git a/src/routes/milestoneTemplates/list.spec.js b/src/routes/milestoneTemplates/list.spec.js index 086356c3..29f8d2b7 100644 --- a/src/routes/milestoneTemplates/list.spec.js +++ b/src/routes/milestoneTemplates/list.spec.js @@ -69,7 +69,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 1, updatedBy: 2, }, @@ -83,7 +85,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, }, @@ -97,7 +101,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 3', activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, deletedAt: new Date(), @@ -107,29 +113,20 @@ const milestoneTemplates = [ describe('LIST milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('GET /productTemplates/{productTemplateId}/milestones', () => { + describe('GET /timelines/metadata/milestoneTemplates', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates') .expect(403, done); }); - it('should return 422 for invalid productTemplateId param', (done) => { - request(server) - .get('/v4/productTemplates/0/milestones') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .expect(422, done); - }); - it('should return 422 for invalid sort column', (done) => { request(server) - .get('/v4/productTemplates/1/milestones?sort=id') + .get('/v4/timelines/metadata/milestoneTemplates?sort=id') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -138,7 +135,7 @@ describe('LIST milestone template', () => { it('should return 422 for invalid sort order', (done) => { request(server) - .get('/v4/productTemplates/1/milestones?sort=order%20invalid') + .get('/v4/timelines/metadata/milestoneTemplates?sort=order%20invalid') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -147,7 +144,7 @@ describe('LIST milestone template', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -164,7 +161,9 @@ describe('LIST milestone template', () => { resJson[0].blockedText.should.be.eql(milestoneTemplates[0].blockedText); resJson[0].activeText.should.be.eql(milestoneTemplates[0].activeText); resJson[0].completedText.should.be.eql(milestoneTemplates[0].completedText); - resJson[0].productTemplateId.should.be.eql(milestoneTemplates[0].productTemplateId); + resJson[0].reference.should.be.eql(milestoneTemplates[0].reference); + resJson[0].referenceId.should.be.eql(milestoneTemplates[0].referenceId); + resJson[0].metadata.should.be.eql(milestoneTemplates[0].metadata); resJson[0].createdBy.should.be.eql(milestoneTemplates[0].createdBy); should.exist(resJson[0].createdAt); @@ -177,9 +176,9 @@ describe('LIST milestone template', () => { }); }); - it('should return 200 for connect admin', (done) => { + it('should return 200 for connect admin with filter', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -189,7 +188,7 @@ describe('LIST milestone template', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -199,7 +198,7 @@ describe('LIST milestone template', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -208,7 +207,7 @@ describe('LIST milestone template', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/productTemplates/1/milestones') + .get('/v4/timelines/metadata/milestoneTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -217,7 +216,7 @@ describe('LIST milestone template', () => { it('should return 200 with sort desc', (done) => { request(server) - .get('/v4/productTemplates/1/milestones?sort=order%20desc') + .get('/v4/timelines/metadata/milestoneTemplates?sort=order%20desc') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/milestoneTemplates/update.js b/src/routes/milestoneTemplates/update.js index 8936b72c..8b60369d 100644 --- a/src/routes/milestoneTemplates/update.js +++ b/src/routes/milestoneTemplates/update.js @@ -8,12 +8,13 @@ import Sequelize from 'sequelize'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import models from '../../models'; +import validateMilestoneTemplate from '../../middlewares/validateMilestoneTemplate'; +import { MILESTONE_TEMPLATE_REFERENCES } from '../../constants'; const permissions = tcMiddleware.permissions; const schema = { params: { - productTemplateId: Joi.number().integer().positive().required(), milestoneTemplateId: Joi.number().integer().positive().required(), }, body: { @@ -30,6 +31,9 @@ const schema = { blockedText: Joi.string().max(512).optional(), productTemplateId: Joi.any().strip(), hidden: Joi.boolean().optional(), + reference: Joi.string().valid(_.values(MILESTONE_TEMPLATE_REFERENCES)).required(), + referenceId: Joi.number().integer().positive().required(), + metadata: Joi.object().optional(), createdAt: Joi.any().strip(), updatedAt: Joi.any().strip(), deletedAt: Joi.any().strip(), @@ -42,37 +46,20 @@ const schema = { module.exports = [ validate(schema), + validateMilestoneTemplate.validateIdParam, + validateMilestoneTemplate.validateRequestBody, permissions('milestoneTemplate.edit'), (req, res, next) => { const entityToUpdate = _.assign(req.body.param, { updatedBy: req.authUser.userId, }); - let original; + const original = _.omit(req.milestoneTemplate.toJSON(), 'deletedAt', 'deletedBy'); let updated; return models.sequelize.transaction(() => - // Get the milestone template - models.ProductMilestoneTemplate.findOne({ - where: { - id: req.params.milestoneTemplateId, - productTemplateId: req.params.productTemplateId, - }, - attributes: { exclude: ['deletedAt', 'deletedBy'] }, - }) - .then((milestoneTemplate) => { - // Not found - if (!milestoneTemplate) { - const apiErr = new Error(`Milestone template not found for template id ${req.params.milestoneTemplateId}`); - apiErr.status = 404; - return Promise.reject(apiErr); - } - - original = _.omit(milestoneTemplate.toJSON(), ['deletedAt', 'deletedBy']); - - // Update - return milestoneTemplate.update(entityToUpdate); - }) + // Update + req.milestoneTemplate.update(entityToUpdate) .then((milestoneTemplate) => { updated = _.omit(milestoneTemplate.toJSON(), ['deletedAt', 'deletedBy']); @@ -81,9 +68,10 @@ module.exports = [ return Promise.resolve(); } - return models.ProductMilestoneTemplate.count({ + return models.MilestoneTemplate.count({ where: { - productTemplateId: updated.productTemplateId, + reference: updated.reference, + referenceId: updated.referenceId, id: { $ne: updated.id }, order: updated.order, }, @@ -96,9 +84,10 @@ module.exports = [ // Increase the order from M to K: if there is an item with order K, // orders from M+1 to K should be made M to K-1 if (original.order < updated.order) { - return models.ProductMilestoneTemplate.update({ order: Sequelize.literal('"order" - 1') }, { + return models.MilestoneTemplate.update({ order: Sequelize.literal('"order" - 1') }, { where: { - productTemplateId: updated.productTemplateId, + reference: updated.reference, + referenceId: updated.referenceId, id: { $ne: updated.id }, order: { $between: [original.order + 1, updated.order] }, }, @@ -107,9 +96,10 @@ module.exports = [ // Decrease the order from M to K: if there is an item with order K, // orders from K to M-1 should be made K+1 to M - return models.ProductMilestoneTemplate.update({ order: Sequelize.literal('"order" + 1') }, { + return models.MilestoneTemplate.update({ order: Sequelize.literal('"order" + 1') }, { where: { - productTemplateId: updated.productTemplateId, + reference: updated.reference, + referenceId: updated.referenceId, id: { $ne: updated.id }, order: { $between: [updated.order, original.order - 1] }, }, @@ -117,10 +107,10 @@ module.exports = [ }); }), ) - .then(() => { - res.json(util.wrapResponse(req.id, updated)); - return Promise.resolve(); - }) - .catch(next); + .then(() => { + res.json(util.wrapResponse(req.id, updated)); + return Promise.resolve(); + }) + .catch(next); }, ]; diff --git a/src/routes/milestoneTemplates/update.spec.js b/src/routes/milestoneTemplates/update.spec.js index 59bbcb00..bf14b304 100644 --- a/src/routes/milestoneTemplates/update.spec.js +++ b/src/routes/milestoneTemplates/update.spec.js @@ -69,7 +69,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 1, updatedBy: 2, }, @@ -83,7 +85,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, }, @@ -97,7 +101,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 3', activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, }, @@ -111,7 +117,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 4', activeText: 'text to be shown in active stage - 4', completedText: 'text to be shown in completed stage - 4', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, deletedAt: new Date(), @@ -121,11 +129,11 @@ const milestoneTemplates = [ describe('UPDATE milestone template', () => { beforeEach(() => testUtil.clearDb() .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)), + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)), ); after(testUtil.clearDb); - describe('PATCH /productTemplates/{productTemplateId}/milestones/{milestoneTemplateId}', () => { + describe('PATCH /timelines/metadata/milestoneTemplates/{milestoneTemplateId}', () => { const body = { param: { name: 'milestoneTemplate 1-updated', @@ -138,19 +146,22 @@ describe('UPDATE milestone template', () => { activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', hidden: true, + reference: 'product', + referenceId: 1, + metadata: {}, }, }; it('should return 403 if user is not authenticated', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -160,7 +171,7 @@ describe('UPDATE milestone template', () => { it('should return 403 for copilot', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, @@ -170,7 +181,7 @@ describe('UPDATE milestone template', () => { it('should return 403 for manager', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, @@ -178,19 +189,9 @@ describe('UPDATE milestone template', () => { .expect(403, done); }); - it('should return 404 for non-existed product template', (done) => { - request(server) - .patch('/v4/productTemplates/122/milestones/1') - .set({ - Authorization: `Bearer ${testUtil.jwts.admin}`, - }) - .send(body) - .expect(404, done); - }); - it('should return 404 for non-existed milestone template', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/111') + .patch('/v4/timelines/metadata/milestoneTemplates/111') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -200,7 +201,7 @@ describe('UPDATE milestone template', () => { it('should return 404 for deleted milestone template', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/4') + .patch('/v4/timelines/metadata/milestoneTemplates/4') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -210,7 +211,7 @@ describe('UPDATE milestone template', () => { it('should return 200 for admin', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -228,6 +229,9 @@ describe('UPDATE milestone template', () => { resJson.blockedText.should.be.eql(body.param.blockedText); resJson.activeText.should.be.eql(body.param.activeText); resJson.completedText.should.be.eql(body.param.completedText); + resJson.reference.should.be.eql(body.param.reference); + resJson.referenceId.should.be.eql(body.param.referenceId); + resJson.metadata.should.be.eql(body.param.metadata); should.exist(resJson.createdBy); should.exist(resJson.createdAt); @@ -245,7 +249,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -255,15 +259,15 @@ describe('UPDATE milestone template', () => { // Milestone 1: order 3 // Milestone 2: order 2 - 1 = 1 // Milestone 3: order 3 - 1 = 2 - models.ProductMilestoneTemplate.findById(1) + models.MilestoneTemplate.findById(1) .then((milestone) => { milestone.order.should.be.eql(3); }) - .then(() => models.ProductMilestoneTemplate.findById(2)) + .then(() => models.MilestoneTemplate.findById(2)) .then((milestone) => { milestone.order.should.be.eql(1); }) - .then(() => models.ProductMilestoneTemplate.findById(3)) + .then(() => models.MilestoneTemplate.findById(3)) .then((milestone) => { milestone.order.should.be.eql(2); @@ -277,7 +281,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -287,15 +291,15 @@ describe('UPDATE milestone template', () => { // Milestone 1: order 4 // Milestone 2: order 2 // Milestone 3: order 3 - models.ProductMilestoneTemplate.findById(1) + models.MilestoneTemplate.findById(1) .then((milestone) => { milestone.order.should.be.eql(4); }) - .then(() => models.ProductMilestoneTemplate.findById(2)) + .then(() => models.MilestoneTemplate.findById(2)) .then((milestone) => { milestone.order.should.be.eql(2); }) - .then(() => models.ProductMilestoneTemplate.findById(3)) + .then(() => models.MilestoneTemplate.findById(3)) .then((milestone) => { milestone.order.should.be.eql(3); @@ -309,7 +313,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/productTemplates/1/milestones/3') + .patch('/v4/timelines/metadata/milestoneTemplates/3') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -319,15 +323,15 @@ describe('UPDATE milestone template', () => { // Milestone 1: order 2 // Milestone 2: order 3 // Milestone 3: order 1 - models.ProductMilestoneTemplate.findById(1) + models.MilestoneTemplate.findById(1) .then((milestone) => { milestone.order.should.be.eql(2); }) - .then(() => models.ProductMilestoneTemplate.findById(2)) + .then(() => models.MilestoneTemplate.findById(2)) .then((milestone) => { milestone.order.should.be.eql(3); }) - .then(() => models.ProductMilestoneTemplate.findById(3)) + .then(() => models.MilestoneTemplate.findById(3)) .then((milestone) => { milestone.order.should.be.eql(1); @@ -341,7 +345,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/productTemplates/1/milestones/3') + .patch('/v4/timelines/metadata/milestoneTemplates/3') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -351,15 +355,15 @@ describe('UPDATE milestone template', () => { // Milestone 1: order 1 // Milestone 2: order 2 // Milestone 3: order 0 - models.ProductMilestoneTemplate.findById(1) + models.MilestoneTemplate.findById(1) .then((milestone) => { milestone.order.should.be.eql(1); }) - .then(() => models.ProductMilestoneTemplate.findById(2)) + .then(() => models.MilestoneTemplate.findById(2)) .then((milestone) => { milestone.order.should.be.eql(2); }) - .then(() => models.ProductMilestoneTemplate.findById(3)) + .then(() => models.MilestoneTemplate.findById(3)) .then((milestone) => { milestone.order.should.be.eql(0); @@ -372,7 +376,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.name; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -384,7 +388,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.type; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -396,7 +400,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.duration; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -408,7 +412,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.order; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -420,7 +424,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.plannedText; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -432,7 +436,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.blockedText; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -444,7 +448,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.activeText; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -456,7 +460,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.completedText; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -468,7 +472,7 @@ describe('UPDATE milestone template', () => { const partialBody = _.cloneDeep(body); delete partialBody.param.hidden; request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -478,7 +482,7 @@ describe('UPDATE milestone template', () => { it('should return 200 for connect admin', (done) => { request(server) - .patch('/v4/productTemplates/1/milestones/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productCategories/create.spec.js b/src/routes/productCategories/create.spec.js index 7b8f0089..c345eda3 100644 --- a/src/routes/productCategories/create.spec.js +++ b/src/routes/productCategories/create.spec.js @@ -28,7 +28,7 @@ describe('CREATE product category', () => { ); after(testUtil.clearDb); - describe('POST /productCategories', () => { + describe('POST /projects/metadata/productCategories', () => { const body = { param: { key: 'app_dev', @@ -44,14 +44,14 @@ describe('CREATE product category', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -61,7 +61,7 @@ describe('CREATE product category', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -71,7 +71,7 @@ describe('CREATE product category', () => { it('should return 403 for manager', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -84,7 +84,7 @@ describe('CREATE product category', () => { delete invalidBody.param.key; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -98,7 +98,7 @@ describe('CREATE product category', () => { delete invalidBody.param.displayName; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -112,7 +112,7 @@ describe('CREATE product category', () => { delete invalidBody.param.icon; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -126,7 +126,7 @@ describe('CREATE product category', () => { delete invalidBody.param.question; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -140,7 +140,7 @@ describe('CREATE product category', () => { delete invalidBody.param.info; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -154,7 +154,7 @@ describe('CREATE product category', () => { invalidBody.param.key = 'key1'; request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -165,7 +165,7 @@ describe('CREATE product category', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -196,7 +196,7 @@ describe('CREATE product category', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/productCategories') + .post('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productCategories/delete.spec.js b/src/routes/productCategories/delete.spec.js index dc33a92f..40f8baa5 100644 --- a/src/routes/productCategories/delete.spec.js +++ b/src/routes/productCategories/delete.spec.js @@ -26,7 +26,7 @@ const expectAfterDelete = (key, err, next) => { chai.assert.isNotNull(res.deletedBy); request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -52,16 +52,16 @@ describe('DELETE product category', () => { ); after(testUtil.clearDb); - describe('DELETE /productCategories/{key}', () => { + describe('DELETE /projects/metadata/productCategories/{key}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -70,7 +70,7 @@ describe('DELETE product category', () => { it('should return 403 for copilot', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -79,7 +79,7 @@ describe('DELETE product category', () => { it('should return 403 for manager', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -88,7 +88,7 @@ describe('DELETE product category', () => { it('should return 404 for non-existed product category', (done) => { request(server) - .delete('/v4/productCategories/not_existed') + .delete('/v4/projects/metadata/productCategories/not_existed') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -99,7 +99,7 @@ describe('DELETE product category', () => { models.ProductCategory.destroy({ where: { key } }) .then(() => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -109,7 +109,7 @@ describe('DELETE product category', () => { it('should return 204, for admin, if the product category was successfully removed', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -119,7 +119,7 @@ describe('DELETE product category', () => { it('should return 204, for connect admin, if the product category was successfully removed', (done) => { request(server) - .delete(`/v4/productCategories/${key}`) + .delete(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productCategories/get.spec.js b/src/routes/productCategories/get.spec.js index 9d06dbf4..78ba07ce 100644 --- a/src/routes/productCategories/get.spec.js +++ b/src/routes/productCategories/get.spec.js @@ -32,10 +32,10 @@ describe('GET product category', () => { ); after(testUtil.clearDb); - describe('GET /productCategories/{key}', () => { + describe('GET /projects/metadata/productCategories/{key}', () => { it('should return 404 for non-existed product category', (done) => { request(server) - .get('/v4/productCategories/1234') + .get('/v4/projects/metadata/productCategories/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -46,7 +46,7 @@ describe('GET product category', () => { models.ProductCategory.destroy({ where: { key } }) .then(() => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -56,7 +56,7 @@ describe('GET product category', () => { it('should return 200 for admin', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -84,13 +84,13 @@ describe('GET product category', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -100,7 +100,7 @@ describe('GET product category', () => { it('should return 200 for connect manager', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -110,7 +110,7 @@ describe('GET product category', () => { it('should return 200 for member', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -119,7 +119,7 @@ describe('GET product category', () => { it('should return 200 for copilot', (done) => { request(server) - .get(`/v4/productCategories/${key}`) + .get(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/productCategories/list.spec.js b/src/routes/productCategories/list.spec.js index e0e56ec7..d055f7df 100644 --- a/src/routes/productCategories/list.spec.js +++ b/src/routes/productCategories/list.spec.js @@ -45,10 +45,10 @@ describe('LIST product categories', () => { ); after(testUtil.clearDb); - describe('GET /productCategories', () => { + describe('GET /projects/metadata/productCategories', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -79,13 +79,13 @@ describe('LIST product categories', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -95,7 +95,7 @@ describe('LIST product categories', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -105,7 +105,7 @@ describe('LIST product categories', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -114,7 +114,7 @@ describe('LIST product categories', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/productCategories') + .get('/v4/projects/metadata/productCategories') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/productCategories/update.spec.js b/src/routes/productCategories/update.spec.js index 7b877553..ad9a43b5 100644 --- a/src/routes/productCategories/update.spec.js +++ b/src/routes/productCategories/update.spec.js @@ -32,7 +32,7 @@ describe('UPDATE product category', () => { ); after(testUtil.clearDb); - describe('PATCH /productCategories/{key}', () => { + describe('PATCH /projects/metadata/productCategories/{key}', () => { const body = { param: { displayName: 'displayName 1 - update', @@ -47,14 +47,14 @@ describe('UPDATE product category', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -64,7 +64,7 @@ describe('UPDATE product category', () => { it('should return 403 for copilot', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, @@ -74,7 +74,7 @@ describe('UPDATE product category', () => { it('should return 403 for manager', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, @@ -84,7 +84,7 @@ describe('UPDATE product category', () => { it('should return 404 for non-existed product category', (done) => { request(server) - .patch('/v4/productCategories/1234') + .patch('/v4/projects/metadata/productCategories/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -96,7 +96,7 @@ describe('UPDATE product category', () => { models.ProductCategory.destroy({ where: { key } }) .then(() => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -114,7 +114,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -150,7 +150,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -186,7 +186,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -222,7 +222,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -258,7 +258,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -294,7 +294,7 @@ describe('UPDATE product category', () => { delete partialBody.param.aliases; delete partialBody.param.hidden; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -330,7 +330,7 @@ describe('UPDATE product category', () => { delete partialBody.param.disabled; delete partialBody.param.aliases; request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -358,7 +358,7 @@ describe('UPDATE product category', () => { it('should return 200 for admin all fields updated', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -386,7 +386,7 @@ describe('UPDATE product category', () => { it('should return 200 for connect admin', (done) => { request(server) - .patch(`/v4/productCategories/${key}`) + .patch(`/v4/projects/metadata/productCategories/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productTemplates/create.spec.js b/src/routes/productTemplates/create.spec.js index 846e429f..b4da9239 100644 --- a/src/routes/productTemplates/create.spec.js +++ b/src/routes/productTemplates/create.spec.js @@ -29,7 +29,7 @@ describe('CREATE product template', () => { .then(() => done()); }); - describe('POST /productTemplates', () => { + describe('POST /projects/metadata/productTemplates', () => { const body = { param: { name: 'name 1', @@ -62,14 +62,14 @@ describe('CREATE product template', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -79,7 +79,7 @@ describe('CREATE product template', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -89,7 +89,7 @@ describe('CREATE product template', () => { it('should return 403 for connect manager', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -106,7 +106,7 @@ describe('CREATE product template', () => { }; request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -119,7 +119,7 @@ describe('CREATE product template', () => { const invalidBody = _.cloneDeep(body); invalidBody.param.category = null; request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -132,7 +132,7 @@ describe('CREATE product template', () => { const invalidBody = _.cloneDeep(body); invalidBody.param.category = 'not_exist'; request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -143,7 +143,7 @@ describe('CREATE product template', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -177,7 +177,7 @@ describe('CREATE product template', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/productTemplates') + .post('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productTemplates/delete.spec.js b/src/routes/productTemplates/delete.spec.js index 0f39eb15..edb53d74 100644 --- a/src/routes/productTemplates/delete.spec.js +++ b/src/routes/productTemplates/delete.spec.js @@ -25,7 +25,7 @@ const expectAfterDelete = (id, err, next) => { chai.assert.isNotNull(res.deletedBy); request(server) - .get(`/v4/productTemplates/${id}`) + .get(`/v4/projects/metadata/productTemplates/${id}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -72,16 +72,16 @@ describe('DELETE product template', () => { ); after(testUtil.clearDb); - describe('DELETE /productTemplates/{templateId}', () => { + describe('DELETE /projects/metadata/productTemplates/{templateId}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -90,7 +90,7 @@ describe('DELETE product template', () => { it('should return 403 for copilot', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -99,7 +99,7 @@ describe('DELETE product template', () => { it('should return 403 for connect manager', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -108,7 +108,7 @@ describe('DELETE product template', () => { it('should return 404 for non-existed template', (done) => { request(server) - .delete('/v4/productTemplates/1234') + .delete('/v4/projects/metadata/productTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -119,7 +119,7 @@ describe('DELETE product template', () => { models.ProductTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -129,7 +129,7 @@ describe('DELETE product template', () => { it('should return 204, for admin, if template was successfully removed', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -139,7 +139,7 @@ describe('DELETE product template', () => { it('should return 204, for connect admin, if template was successfully removed', (done) => { request(server) - .delete(`/v4/productTemplates/${templateId}`) + .delete(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/productTemplates/get.spec.js b/src/routes/productTemplates/get.spec.js index c690efca..42b34e30 100644 --- a/src/routes/productTemplates/get.spec.js +++ b/src/routes/productTemplates/get.spec.js @@ -56,10 +56,10 @@ describe('GET product template', () => { ); after(testUtil.clearDb); - describe('GET /productTemplates/{templateId}', () => { + describe('GET /projects/metadata/productTemplates/{templateId}', () => { it('should return 404 for non-existed template', (done) => { request(server) - .get('/v4/productTemplates/1234') + .get('/v4/projects/metadata/productTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -70,7 +70,7 @@ describe('GET product template', () => { models.ProductTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -80,7 +80,7 @@ describe('GET product template', () => { it('should return 200 for admin', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -110,13 +110,13 @@ describe('GET product template', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -126,7 +126,7 @@ describe('GET product template', () => { it('should return 200 for connect manager', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -136,7 +136,7 @@ describe('GET product template', () => { it('should return 200 for member', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -145,7 +145,7 @@ describe('GET product template', () => { it('should return 200 for copilot', (done) => { request(server) - .get(`/v4/productTemplates/${templateId}`) + .get(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/productTemplates/list.spec.js b/src/routes/productTemplates/list.spec.js index b0e8c56f..9282e409 100644 --- a/src/routes/productTemplates/list.spec.js +++ b/src/routes/productTemplates/list.spec.js @@ -94,10 +94,10 @@ describe('LIST product templates', () => { ); after(testUtil.clearDb); - describe('GET /productTemplates', () => { + describe('GET /projects/metadata/productTemplates', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -112,7 +112,7 @@ describe('LIST product templates', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .expect(200) .end((err, res) => { const resJson = res.body.result.content; @@ -124,7 +124,7 @@ describe('LIST product templates', () => { it('should return 200 for connect admin', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -139,7 +139,7 @@ describe('LIST product templates', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -154,7 +154,7 @@ describe('LIST product templates', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -168,7 +168,7 @@ describe('LIST product templates', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/productTemplates') + .get('/v4/projects/metadata/productTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -182,7 +182,7 @@ describe('LIST product templates', () => { it('should return filtered templates', (done) => { request(server) - .get('/v4/productTemplates?filter=productKey%3DproductKey-2') + .get('/v4/projects/metadata/productTemplates?filter=productKey%3DproductKey-2') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) diff --git a/src/routes/productTemplates/update.spec.js b/src/routes/productTemplates/update.spec.js index 80667f0f..6e65461d 100644 --- a/src/routes/productTemplates/update.spec.js +++ b/src/routes/productTemplates/update.spec.js @@ -80,7 +80,7 @@ describe('UPDATE product template', () => { ); after(testUtil.clearDb); - describe('PATCH /productTemplates/{templateId}', () => { + describe('PATCH /projects/metadata/productTemplates/{templateId}', () => { const body = { param: { name: 'template 1 - update', @@ -119,14 +119,14 @@ describe('UPDATE product template', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -136,7 +136,7 @@ describe('UPDATE product template', () => { it('should return 403 for copilot', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, @@ -146,7 +146,7 @@ describe('UPDATE product template', () => { it('should return 403 for connect manager', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, @@ -163,7 +163,7 @@ describe('UPDATE product template', () => { }; request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -173,7 +173,7 @@ describe('UPDATE product template', () => { it('should return 404 for non-existed template', (done) => { request(server) - .patch('/v4/productTemplates/1234') + .patch('/v4/projects/metadata/productTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -185,7 +185,7 @@ describe('UPDATE product template', () => { models.ProductTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -196,7 +196,7 @@ describe('UPDATE product template', () => { it('should return 200 for admin', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -260,7 +260,7 @@ describe('UPDATE product template', () => { it('should return 200 for connect admin', (done) => { request(server) - .patch(`/v4/productTemplates/${templateId}`) + .patch(`/v4/projects/metadata/productTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectMembers/delete.js b/src/routes/projectMembers/delete.js index 43e1f037..b58aaa84 100644 --- a/src/routes/projectMembers/delete.js +++ b/src/routes/projectMembers/delete.js @@ -28,9 +28,9 @@ module.exports = [ err.status = 404; return Promise.reject(err); } - return member.update({ deletedBy: req.authUser.userId }); // eslint-disable-line no-console + return member.update({ deletedBy: req.authUser.userId }); }) - .then(member => member.destroy({ logging: console.log })) + .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) => { diff --git a/src/routes/projectTemplates/create.spec.js b/src/routes/projectTemplates/create.spec.js index c18123cb..51989b4c 100644 --- a/src/routes/projectTemplates/create.spec.js +++ b/src/routes/projectTemplates/create.spec.js @@ -30,7 +30,7 @@ describe('CREATE project template', () => { .then(() => done()); }); - describe('POST /projectTemplates', () => { + describe('POST /projects/metadata/projectTemplates', () => { const body = { param: { name: 'template 1', @@ -70,14 +70,14 @@ describe('CREATE project template', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -87,7 +87,7 @@ describe('CREATE project template', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -97,7 +97,7 @@ describe('CREATE project template', () => { it('should return 403 for connect manager', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -114,7 +114,7 @@ describe('CREATE project template', () => { }; request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -127,7 +127,7 @@ describe('CREATE project template', () => { const invalidBody = _.cloneDeep(body); invalidBody.param.type = null; request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -140,7 +140,7 @@ describe('CREATE project template', () => { const invalidBody = _.cloneDeep(body); invalidBody.param.type = 'not_exist'; request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -151,7 +151,7 @@ describe('CREATE project template', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -182,7 +182,7 @@ describe('CREATE project template', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/projectTemplates') + .post('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectTemplates/delete.spec.js b/src/routes/projectTemplates/delete.spec.js index a475ee5d..8c3c268f 100644 --- a/src/routes/projectTemplates/delete.spec.js +++ b/src/routes/projectTemplates/delete.spec.js @@ -25,7 +25,7 @@ const expectAfterDelete = (id, err, next) => { chai.assert.isNotNull(res.deletedBy); request(server) - .get(`/v4/projectTemplates/${id}`) + .get(`/v4/projects/metadata/projectTemplates/${id}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -78,16 +78,16 @@ describe('DELETE project template', () => { ); after(testUtil.clearDb); - describe('DELETE /projectTemplates/{templateId}', () => { + describe('DELETE /projects/metadata/projectTemplates/{templateId}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -96,7 +96,7 @@ describe('DELETE project template', () => { it('should return 403 for copilot', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -105,7 +105,7 @@ describe('DELETE project template', () => { it('should return 403 for connect manager', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -114,7 +114,7 @@ describe('DELETE project template', () => { it('should return 404 for non-existed template', (done) => { request(server) - .delete('/v4/projectTemplates/1234') + .delete('/v4/projects/metadata/projectTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -125,7 +125,7 @@ describe('DELETE project template', () => { models.ProjectTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -135,7 +135,7 @@ describe('DELETE project template', () => { it('should return 204, for admin, if template was successfully removed', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -144,7 +144,7 @@ describe('DELETE project template', () => { it('should return 204, for connect admin, if template was successfully removed', (done) => { request(server) - .delete(`/v4/projectTemplates/${templateId}`) + .delete(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectTemplates/get.spec.js b/src/routes/projectTemplates/get.spec.js index 50899ec4..ae6607fa 100644 --- a/src/routes/projectTemplates/get.spec.js +++ b/src/routes/projectTemplates/get.spec.js @@ -57,10 +57,10 @@ describe('GET project template', () => { ); after(testUtil.clearDb); - describe('GET /projectTemplates/{templateId}', () => { + describe('GET /projects/metadata/projectTemplates/{templateId}', () => { it('should return 404 for non-existed template', (done) => { request(server) - .get('/v4/projectTemplates/1234') + .get('/v4/projects/metadata/projectTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -71,7 +71,7 @@ describe('GET project template', () => { models.ProjectTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -81,7 +81,7 @@ describe('GET project template', () => { it('should return 200 for admin', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -107,13 +107,13 @@ describe('GET project template', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -123,7 +123,7 @@ describe('GET project template', () => { it('should return 200 for connect manager', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -133,7 +133,7 @@ describe('GET project template', () => { it('should return 200 for member', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -142,7 +142,7 @@ describe('GET project template', () => { it('should return 200 for copilot', (done) => { request(server) - .get(`/v4/projectTemplates/${templateId}`) + .get(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/projectTemplates/list.spec.js b/src/routes/projectTemplates/list.spec.js index e826ea9f..9c3008e8 100644 --- a/src/routes/projectTemplates/list.spec.js +++ b/src/routes/projectTemplates/list.spec.js @@ -74,10 +74,10 @@ describe('LIST project templates', () => { ); after(testUtil.clearDb); - describe('GET /projectTemplates', () => { + describe('GET /projects/metadata/projectTemplates', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -106,13 +106,13 @@ describe('LIST project templates', () => { it('should return 200 for anonymous user', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -122,7 +122,7 @@ describe('LIST project templates', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -132,7 +132,7 @@ describe('LIST project templates', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -141,7 +141,7 @@ describe('LIST project templates', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/projectTemplates') + .get('/v4/projects/metadata/projectTemplates') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/projectTemplates/update.spec.js b/src/routes/projectTemplates/update.spec.js index 514df753..ff051381 100644 --- a/src/routes/projectTemplates/update.spec.js +++ b/src/routes/projectTemplates/update.spec.js @@ -83,7 +83,7 @@ describe('UPDATE project template', () => { ); after(testUtil.clearDb); - describe('PATCH /projectTemplates/{templateId}', () => { + describe('PATCH /projects/metadata/projectTemplates/{templateId}', () => { const body = { param: { name: 'template 1 - update', @@ -119,14 +119,14 @@ describe('UPDATE project template', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -136,7 +136,7 @@ describe('UPDATE project template', () => { it('should return 403 for copilot', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, @@ -146,7 +146,7 @@ describe('UPDATE project template', () => { it('should return 403 for connect manager', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, @@ -163,7 +163,7 @@ describe('UPDATE project template', () => { }; request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -173,7 +173,7 @@ describe('UPDATE project template', () => { it('should return 404 for non-existed template', (done) => { request(server) - .patch('/v4/projectTemplates/1234') + .patch('/v4/projects/metadata/projectTemplates/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -185,7 +185,7 @@ describe('UPDATE project template', () => { models.ProjectTemplate.destroy({ where: { id: templateId } }) .then(() => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -196,7 +196,7 @@ describe('UPDATE project template', () => { it('should return 200 for admin', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -256,7 +256,7 @@ describe('UPDATE project template', () => { it('should return 200 for connect admin', (done) => { request(server) - .patch(`/v4/projectTemplates/${templateId}`) + .patch(`/v4/projects/metadata/projectTemplates/${templateId}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectTypes/create.spec.js b/src/routes/projectTypes/create.spec.js index d2e339bc..7823e45b 100644 --- a/src/routes/projectTypes/create.spec.js +++ b/src/routes/projectTypes/create.spec.js @@ -29,7 +29,7 @@ describe('CREATE project type', () => { ); after(testUtil.clearDb); - describe('POST /projectTypes', () => { + describe('POST /projects/metadata/projectTypes', () => { const body = { param: { key: 'app_dev', @@ -46,14 +46,14 @@ describe('CREATE project type', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -63,7 +63,7 @@ describe('CREATE project type', () => { it('should return 403 for copilot', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -73,7 +73,7 @@ describe('CREATE project type', () => { it('should return 403 for manager', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -86,7 +86,7 @@ describe('CREATE project type', () => { delete invalidBody.param.key; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -100,7 +100,7 @@ describe('CREATE project type', () => { delete invalidBody.param.displayName; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -114,7 +114,7 @@ describe('CREATE project type', () => { delete invalidBody.param.icon; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -128,7 +128,7 @@ describe('CREATE project type', () => { delete invalidBody.param.question; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -142,7 +142,7 @@ describe('CREATE project type', () => { delete invalidBody.param.info; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -156,7 +156,7 @@ describe('CREATE project type', () => { delete invalidBody.param.metadata; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -170,7 +170,7 @@ describe('CREATE project type', () => { invalidBody.param.key = 'key1'; request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -181,7 +181,7 @@ describe('CREATE project type', () => { it('should return 201 for admin', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -213,7 +213,7 @@ describe('CREATE project type', () => { it('should return 201 for connect admin', (done) => { request(server) - .post('/v4/projectTypes') + .post('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectTypes/delete.spec.js b/src/routes/projectTypes/delete.spec.js index 26b35994..5b9cee54 100644 --- a/src/routes/projectTypes/delete.spec.js +++ b/src/routes/projectTypes/delete.spec.js @@ -24,7 +24,7 @@ const expectAfterDelete = (key, err, next) => { chai.assert.isNotNull(res.deletedBy); request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -51,16 +51,16 @@ describe('DELETE project type', () => { ); after(testUtil.clearDb); - describe('DELETE /projectTypes/{key}', () => { + describe('DELETE /projects/metadata/projectTypes/{key}', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -69,7 +69,7 @@ describe('DELETE project type', () => { it('should return 403 for copilot', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) @@ -78,7 +78,7 @@ describe('DELETE project type', () => { it('should return 403 for manager', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -87,7 +87,7 @@ describe('DELETE project type', () => { it('should return 404 for non-existed type', (done) => { request(server) - .delete('/v4/projectTypes/not_existed') + .delete('/v4/projects/metadata/projectTypes/not_existed') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -98,7 +98,7 @@ describe('DELETE project type', () => { models.ProjectType.destroy({ where: { key } }) .then(() => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -108,7 +108,7 @@ describe('DELETE project type', () => { it('should return 204, for admin, if type was successfully removed', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -118,7 +118,7 @@ describe('DELETE project type', () => { it('should return 204, for connect admin, if type was successfully removed', (done) => { request(server) - .delete(`/v4/projectTypes/${key}`) + .delete(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/projectTypes/get.spec.js b/src/routes/projectTypes/get.spec.js index eb72604f..099b4c66 100644 --- a/src/routes/projectTypes/get.spec.js +++ b/src/routes/projectTypes/get.spec.js @@ -33,10 +33,10 @@ describe('GET project type', () => { ); after(testUtil.clearDb); - describe('GET /projectTypes/{key}', () => { + describe('GET /projects/metadata/projectTypes/{key}', () => { it('should return 404 for non-existed type', (done) => { request(server) - .get('/v4/projectTypes/1234') + .get('/v4/projects/metadata/projectTypes/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -47,7 +47,7 @@ describe('GET project type', () => { models.ProjectType.destroy({ where: { key } }) .then(() => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -57,7 +57,7 @@ describe('GET project type', () => { it('should return 200 for admin', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -86,13 +86,13 @@ describe('GET project type', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -102,7 +102,7 @@ describe('GET project type', () => { it('should return 200 for connect manager', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -112,7 +112,7 @@ describe('GET project type', () => { it('should return 200 for member', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -121,7 +121,7 @@ describe('GET project type', () => { it('should return 200 for copilot', (done) => { request(server) - .get(`/v4/projectTypes/${key}`) + .get(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/projectTypes/list.spec.js b/src/routes/projectTypes/list.spec.js index 991667b5..f941fe02 100644 --- a/src/routes/projectTypes/list.spec.js +++ b/src/routes/projectTypes/list.spec.js @@ -47,10 +47,10 @@ describe('LIST project types', () => { ); after(testUtil.clearDb); - describe('GET /projectTypes', () => { + describe('GET /projects/metadata/projectTypes', () => { it('should return 200 for admin', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -82,13 +82,13 @@ describe('LIST project types', () => { it('should return 200 even if user is not authenticated', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .expect(200, done); }); it('should return 200 for connect admin', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) @@ -98,7 +98,7 @@ describe('LIST project types', () => { it('should return 200 for connect manager', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, }) @@ -108,7 +108,7 @@ describe('LIST project types', () => { it('should return 200 for member', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -117,7 +117,7 @@ describe('LIST project types', () => { it('should return 200 for copilot', (done) => { request(server) - .get('/v4/projectTypes') + .get('/v4/projects/metadata/projectTypes') .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, }) diff --git a/src/routes/projectTypes/update.spec.js b/src/routes/projectTypes/update.spec.js index 0402020c..229f544a 100644 --- a/src/routes/projectTypes/update.spec.js +++ b/src/routes/projectTypes/update.spec.js @@ -33,7 +33,7 @@ describe('UPDATE project type', () => { ); after(testUtil.clearDb); - describe('PATCH /projectTypes/{key}', () => { + describe('PATCH /projects/metadata/projectTypes/{key}', () => { const body = { param: { displayName: 'displayName 1 - update', @@ -49,14 +49,14 @@ describe('UPDATE project type', () => { it('should return 403 if user is not authenticated', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .send(body) .expect(403, done); }); it('should return 403 for member', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.member}`, }) @@ -66,7 +66,7 @@ describe('UPDATE project type', () => { it('should return 403 for copilot', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.copilot}`, @@ -76,7 +76,7 @@ describe('UPDATE project type', () => { it('should return 403 for manager', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .send(body) .set({ Authorization: `Bearer ${testUtil.jwts.manager}`, @@ -86,7 +86,7 @@ describe('UPDATE project type', () => { it('should return 404 for non-existed type', (done) => { request(server) - .patch('/v4/projectTypes/1234') + .patch('/v4/projects/metadata/projectTypes/1234') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -98,7 +98,7 @@ describe('UPDATE project type', () => { models.ProjectType.destroy({ where: { key } }) .then(() => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -117,7 +117,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -155,7 +155,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -193,7 +193,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -231,7 +231,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -269,7 +269,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -307,7 +307,7 @@ describe('UPDATE project type', () => { delete partialBody.param.hidden; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -345,7 +345,7 @@ describe('UPDATE project type', () => { delete partialBody.param.aliases; delete partialBody.param.metadata; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -382,7 +382,7 @@ describe('UPDATE project type', () => { delete partialBody.param.aliases; delete partialBody.param.hidden; request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -411,7 +411,7 @@ describe('UPDATE project type', () => { it('should return 200 for admin all fields updated', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -440,7 +440,7 @@ describe('UPDATE project type', () => { it('should return 200 for connect admin', (done) => { request(server) - .patch(`/v4/projectTypes/${key}`) + .patch(`/v4/projects/metadata/projectTypes/${key}`) .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/timelines/create.js b/src/routes/timelines/create.js index 7aa53502..fc8b2651 100644 --- a/src/routes/timelines/create.js +++ b/src/routes/timelines/create.js @@ -9,7 +9,8 @@ import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; import validateTimeline from '../../middlewares/validateTimeline'; import models from '../../models'; -import { EVENT, TIMELINE_REFERENCES, MILESTONE_STATUS } from '../../constants'; +import { EVENT, TIMELINE_REFERENCES, MILESTONE_STATUS, MILESTONE_TEMPLATE_REFERENCES } + from '../../constants'; const permissions = tcMiddleware.permissions; @@ -59,9 +60,10 @@ module.exports = [ req.log.debug('Checking templateId %d for creating milestones', templateId); if (templateId) { req.log.debug('Found templateId, finding milestone templates for the template'); - return models.ProductMilestoneTemplate.findAll({ + return models.MilestoneTemplate.findAll({ where: { - productTemplateId: templateId, + reference: MILESTONE_TEMPLATE_REFERENCES.PRODUCT, + referenceId: templateId, deletedAt: { $eq: null }, }, order: [['order', 'asc']], diff --git a/src/routes/timelines/create.spec.js b/src/routes/timelines/create.spec.js index c35d4661..7ce3e9c3 100644 --- a/src/routes/timelines/create.spec.js +++ b/src/routes/timelines/create.spec.js @@ -62,7 +62,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 1, updatedBy: 2, hidden: false, @@ -78,7 +80,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, hidden: false, @@ -94,7 +98,9 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 3', activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', - productTemplateId: 1, + reference: 'product', + referenceId: 1, + metadata: {}, createdBy: 2, updatedBy: 3, hidden: false, @@ -168,7 +174,7 @@ describe('CREATE timeline', () => { }); }) .then(() => models.ProductTemplate.bulkCreate(productTemplates)) - .then(() => models.ProductMilestoneTemplate.bulkCreate(milestoneTemplates)) + .then(() => models.MilestoneTemplate.bulkCreate(milestoneTemplates)) .then(() => { done(); }); diff --git a/src/tests/seed.js b/src/tests/seed.js index ea2ea5fb..581b0598 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -441,13 +441,15 @@ models.sequelize.sync({ force: true }) }, ], { returning: true })) // Product milestone templates - .then(productTemplates => models.ProductMilestoneTemplate.bulkCreate([ + .then(productTemplates => models.MilestoneTemplate.bulkCreate([ { name: 'milestoneTemplate 1', duration: 3, type: 'type1', order: 1, - productTemplateId: productTemplates[0].id, + metadata: {}, + reference: 'product', + referenceId: productTemplates[0].id, activeText: 'activeText 1', completedText: 'completedText 1', blockedText: 'blockedText 1', @@ -460,7 +462,9 @@ models.sequelize.sync({ force: true }) duration: 4, type: 'type2', order: 2, - productTemplateId: productTemplates[0].id, + metadata: {}, + reference: 'product', + referenceId: productTemplates[0].id, activeText: 'activeText 2', completedText: 'completedText 2', blockedText: 'blockedText 2', @@ -584,6 +588,7 @@ models.sequelize.sync({ force: true }) question: 'question 1', info: 'info 1', aliases: ['key-11', 'key_12'], + metadata: {}, }, { key: 'generic', @@ -594,6 +599,7 @@ models.sequelize.sync({ force: true }) question: 'question 2', info: 'info 2', aliases: ['key-21', 'key_22'], + metadata: {}, }, { key: 'visual_prototype', @@ -604,6 +610,7 @@ models.sequelize.sync({ force: true }) question: 'question 3', info: 'info 1', aliases: ['key-31', 'key_32'], + metadata: {}, }, { key: 'visual_design', @@ -614,6 +621,7 @@ models.sequelize.sync({ force: true }) question: 'question 4', info: 'info 4', aliases: ['key-41', 'key_42'], + metadata: {}, }, { key: 'website', @@ -624,6 +632,7 @@ models.sequelize.sync({ force: true }) question: 'question 5', info: 'info 5', aliases: ['key-51', 'key_52'], + metadata: {}, }, { key: 'app', @@ -634,6 +643,7 @@ models.sequelize.sync({ force: true }) question: 'question 6', info: 'info 6', aliases: ['key-61', 'key_62'], + metadata: {}, }, { key: 'quality_assurance', @@ -644,6 +654,7 @@ models.sequelize.sync({ force: true }) question: 'question 7', info: 'info 7', aliases: ['key-71', 'key_72'], + metadata: {}, }, { key: 'chatbot', @@ -654,6 +665,7 @@ models.sequelize.sync({ force: true }) question: 'question 8', info: 'info 8', aliases: ['key-81', 'key_82'], + metadata: {}, }, ])) .then(() => models.ProductCategory.bulkCreate([ diff --git a/swagger.yaml b/swagger.yaml index b95bbe94..9aafb6f1 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -648,7 +648,7 @@ paths: schema: $ref: "#/definitions/ProjectUpgradeResponse" - /projectTemplates: + /projects/metadata/projectTemplates: get: tags: - projectTemplate @@ -692,7 +692,7 @@ paths: schema: $ref: "#/definitions/ErrorModel" - /projectTemplates/{templateId}: + /projects/metadata/projectTemplates/{templateId}: get: tags: - projectTemplate @@ -774,7 +774,7 @@ paths: description: Project template successfully removed - /productTemplates: + /projects/metadata/productTemplates: get: tags: - productTemplate @@ -818,7 +818,7 @@ paths: schema: $ref: "#/definitions/ErrorModel" - /productTemplates/{templateId}: + /projects/metadata/productTemplates/{templateId}: get: tags: - productTemplate @@ -900,7 +900,7 @@ paths: description: Product template successfully removed - /productCategories: + /projects/metadata/productCategories: get: tags: - productCategory @@ -944,7 +944,7 @@ paths: schema: $ref: "#/definitions/ErrorModel" - /productCategories/{key}: + /projects/metadata/productCategories/{key}: get: tags: - productCategory @@ -1023,7 +1023,7 @@ paths: description: Product category successfully removed - /projectTypes: + /projects/metadata/projectTypes: get: tags: - projectType @@ -1067,7 +1067,7 @@ paths: schema: $ref: "#/definitions/ErrorModel" - /projectTypes/{key}: + /projects/metadata/projectTypes/{key}: get: tags: - projectType @@ -1438,12 +1438,10 @@ paths: description: Milestone successfully removed - /productTemplates/{productTemplateId}/milestones: - parameters: - - $ref: "#/parameters/productTemplateIdParam" + /timelines/metadata/milestoneTemplates: get: tags: - - productMilestoneTemplate + - milestoneTemplates operationId: findMilestoneTemplates security: - Bearer: [] @@ -1454,6 +1452,14 @@ paths: description: sort by `order`. Default is `order asc` in: query type: string + - name: filter + required: false + type: string + in: query + description: | + Url encoded list of supported filters + - reference + - referenceId responses: '403': description: No permission or wrong token @@ -1469,7 +1475,7 @@ paths: $ref: "#/definitions/MilestoneTemplateListResponse" post: tags: - - productMilestoneTemplate + - milestoneTemplates operationId: addMilestoneTemplate security: - Bearer: [] @@ -1494,12 +1500,10 @@ paths: schema: $ref: "#/definitions/ErrorModel" - /productTemplates/{productTemplateId}/milestones/clone: - parameters: - - $ref: "#/parameters/productTemplateIdParam" + /timelines/metadata/milestoneTemplates/clone: post: tags: - - productMilestoneTemplate + - milestoneTemplates operationId: cloneMilestoneTemplate security: - Bearer: [] @@ -1529,13 +1533,12 @@ paths: $ref: "#/definitions/ErrorModel" - /productTemplates/{productTemplateId}/milestones/{milestoneTemplateId}: + /timelines/metadata/milestoneTemplates/{milestoneTemplateId}: parameters: - - $ref: "#/parameters/productTemplateIdParam" - $ref: "#/parameters/milestoneTemplateIdParam" get: tags: - - productMilestoneTemplate + - milestoneTemplates description: Retrieve milestone template by id. All user roles can access this endpoint. security: - Bearer: [] @@ -1560,7 +1563,7 @@ paths: patch: tags: - - productMilestoneTemplate + - milestoneTemplates operationId: updateMilestoneTemplate security: - Bearer: [] @@ -1595,7 +1598,7 @@ paths: delete: tags: - - productMilestoneTemplate + - milestoneTemplates description: Remove an existing milestone template. Only connect manager, connect admin, and admin can access this endpoint. security: - Bearer: [] @@ -3427,6 +3430,9 @@ definitions: - duration - type - order + - reference + - referenceId + - metadata properties: name: type: string @@ -3445,6 +3451,20 @@ definitions: type: number format: integer description: the milestone template order + reference: + type: string + enum: + - product + - project + description: the milestone template reference + refereneceId: + type: number + format: long + minimum: 1 + description: the milestone template reference id + metadata: + type: object + description: the milestone template metadata MilestoneTemplateBodyParam: title: Milestone template body param @@ -3459,12 +3479,33 @@ definitions: title: Milestone clone template request object type: object required: - - sourceTemplateId + - sourceReference + - sourceReferenceId + - reference + - referenceId properties: - sourceTemplateId: + sourceReference: + type: string + enum: + - product + - project + description: the source reference to clone the milestone templates from + sourceReferenceId: type: number - format: integer - description: the product template id where to clone the milestone templates from + format: long + minimum: 1 + description: the source reference id to clone the milestone templates from + reference: + type: string + enum: + - product + - project + description: the target reference to clone the milestone templates to + refereneceId: + type: number + format: long + minimum: 1 + description: the target reference id to clone the milestone templates to MilestoneTemplate: title: Milestone template object From d2f08a3a0d901d9eed0f024eaed6ece903ec2286 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Sun, 26 Aug 2018 01:54:16 +0700 Subject: [PATCH 08/77] Add new field `metadata` to the `milestoneTemplate` model #169 --- postman.json | 34 ++++++ src/routes/milestoneTemplates/update.js | 3 + src/routes/milestoneTemplates/update.spec.js | 110 ++++++++++++++++++- src/tests/seed.js | 16 +++ 4 files changed, 160 insertions(+), 3 deletions(-) diff --git a/postman.json b/postman.json index 112660b6..dda1b7a7 100644 --- a/postman.json +++ b/postman.json @@ -4474,6 +4474,40 @@ }, "response": [] }, + { + "name": "Update milestone with metadata", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 5-updated\",\r\n\t\"description\": \"description 5-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type5-updated\",\r\n\t\"order\": 5,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {\r\n \"metadata1\": {\r\n \"name\": \"metadata 1 - update\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 1 - update\",\r\n \"newDetails\": \"new\"\r\n },\r\n \"others\": [\"others new\"]\r\n },\r\n \"metadata3\": {\r\n \"name\": \"metadata 3\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 3\"\r\n },\r\n \"others\": [\"others 31\", \"others 32\"]\r\n }\r\n }\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines", + "metadata", + "milestoneTemplates", + "1" + ] + } + }, + "response": [] + }, { "name": "Delete milestone", "request": { diff --git a/src/routes/milestoneTemplates/update.js b/src/routes/milestoneTemplates/update.js index 8b60369d..aea26df1 100644 --- a/src/routes/milestoneTemplates/update.js +++ b/src/routes/milestoneTemplates/update.js @@ -57,6 +57,9 @@ module.exports = [ const original = _.omit(req.milestoneTemplate.toJSON(), 'deletedAt', 'deletedBy'); let updated; + // Merge JSON field + entityToUpdate.metadata = util.mergeJsonObjects(original.metadata, entityToUpdate.metadata || {}); + return models.sequelize.transaction(() => // Update req.milestoneTemplate.update(entityToUpdate) diff --git a/src/routes/milestoneTemplates/update.spec.js b/src/routes/milestoneTemplates/update.spec.js index bf14b304..6091ae3c 100644 --- a/src/routes/milestoneTemplates/update.spec.js +++ b/src/routes/milestoneTemplates/update.spec.js @@ -124,6 +124,37 @@ const milestoneTemplates = [ updatedBy: 3, deletedAt: new Date(), }, + { + id: 5, + name: 'milestoneTemplate 5', + duration: 5, + type: 'type5', + order: 5, + plannedText: 'text to be shown in planned stage - 5', + blockedText: 'text to be shown in blocked stage - 5', + activeText: 'text to be shown in active stage - 5', + completedText: 'text to be shown in completed stage - 5', + reference: 'product', + referenceId: 1, + metadata: { + metadata1: { + name: 'metadata 1', + details: { + anyDetails: 'any details 1', + }, + others: ['others 11', 'others 12'], + }, + metadata2: { + name: 'metadata 2', + details: { + anyDetails: 'any details 2', + }, + others: ['others 21', 'others 22'], + }, + }, + createdBy: 2, + updatedBy: 3 + } ]; describe('UPDATE milestone template', () => { @@ -281,7 +312,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/timelines/metadata/milestoneTemplates/1') + .patch('/v4/timelines/metadata/milestoneTemplates/1') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -313,7 +344,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/timelines/metadata/milestoneTemplates/3') + .patch('/v4/timelines/metadata/milestoneTemplates/3') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -345,7 +376,7 @@ describe('UPDATE milestone template', () => { this.timeout(10000); request(server) - .patch('/v4/timelines/metadata/milestoneTemplates/3') + .patch('/v4/timelines/metadata/milestoneTemplates/3') .set({ Authorization: `Bearer ${testUtil.jwts.admin}`, }) @@ -490,5 +521,78 @@ describe('UPDATE milestone template', () => { .expect(200) .end(done); }); + + it('should return 200 for admin - updating metadata', (done) => { + const bodyWithMetadata = { + param: { + name: 'milestoneTemplate 5-updated', + description: 'description-updated', + duration: 6, + type: 'type5-updated', + order: 5, + plannedText: 'text to be shown in planned stage', + blockedText: 'text to be shown in blocked stage', + activeText: 'text to be shown in active stage', + completedText: 'text to be shown in completed stage', + hidden: true, + reference: 'product', + referenceId: 1, + metadata: { + metadata1: { + name: 'metadata 1 - update', + details: { + anyDetails: 'any details 1 - update', + newDetails: 'new', + }, + others: ['others new'], + }, + metadata3: { + name: 'metadata 3', + details: { + anyDetails: 'any details 3', + }, + others: ['others 31', 'others 32'], + }, + }, + }, + }; + + request(server) + .patch('/v4/timelines/metadata/milestoneTemplates/5') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send(bodyWithMetadata) + .expect(200) + .end((err, res) => { + const resJson = res.body.result.content; + resJson.metadata.should.be.eql({ + metadata1: { + name: 'metadata 1 - update', + details: { + anyDetails: 'any details 1 - update', + newDetails: 'new', + }, + others: ['others new'], + }, + metadata2: { + name: 'metadata 2', + details: { + anyDetails: 'any details 2', + }, + others: ['others 21', 'others 22'], + }, + metadata3: { + name: 'metadata 3', + details: { + anyDetails: 'any details 3', + }, + others: ['others 31', 'others 32'], + }, + }); + + done(); + }); + }); }); }); diff --git a/src/tests/seed.js b/src/tests/seed.js index 581b0598..8fbcd580 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -450,6 +450,22 @@ models.sequelize.sync({ force: true }) metadata: {}, reference: 'product', referenceId: productTemplates[0].id, + metadata: { + metadata1: { + name: 'metadata 1', + details: { + anyDetails: 'any details 1', + }, + others: ['others 11', 'others 12'], + }, + metadata2: { + name: 'metadata 2', + details: { + anyDetails: 'any details 2', + }, + others: ['others 21', 'others 22'], + }, + }, activeText: 'activeText 1', completedText: 'completedText 1', blockedText: 'blockedText 1', From 42df0b03a6000d2b57ead5b3f4bdd8cc1e86aa0e Mon Sep 17 00:00:00 2001 From: ngoctay Date: Sun, 26 Aug 2018 23:49:47 +0700 Subject: [PATCH 09/77] Additional fixes for #151, #161 - support productTemplate as reference and id of the actual productTemplate as referenceId - create a new endpoint which returns all the metadata as GET /v4/projects/metadata --- .../20180824_milestone_templates_metadata.sql | 2 +- postman.json | 55 ++++-- src/constants.js | 3 +- src/middlewares/validateMilestoneTemplate.js | 22 +-- src/permissions/index.js | 2 + src/routes/index.js | 3 + src/routes/metadata/list.js | 36 ++++ src/routes/metadata/list.spec.js | 162 ++++++++++++++++++ src/routes/milestoneTemplates/clone.spec.js | 16 +- src/routes/milestoneTemplates/create.spec.js | 6 +- src/routes/milestoneTemplates/delete.spec.js | 4 +- src/routes/milestoneTemplates/get.spec.js | 4 +- src/routes/milestoneTemplates/list.spec.js | 8 +- src/routes/milestoneTemplates/update.js | 2 +- src/routes/milestoneTemplates/update.spec.js | 18 +- src/routes/timelines/create.js | 2 +- src/tests/seed.js | 5 +- swagger.yaml | 72 +++++++- 18 files changed, 345 insertions(+), 77 deletions(-) create mode 100644 src/routes/metadata/list.js create mode 100644 src/routes/metadata/list.spec.js diff --git a/migrations/20180824_milestone_templates_metadata.sql b/migrations/20180824_milestone_templates_metadata.sql index ef04ac62..8962f98e 100644 --- a/migrations/20180824_milestone_templates_metadata.sql +++ b/migrations/20180824_milestone_templates_metadata.sql @@ -15,7 +15,7 @@ ALTER TABLE product_milestone_templates DROP CONSTRAINT "product_milestone_templ ALTER TABLE product_milestone_templates RENAME COLUMN "productTemplateId" TO referenceId; ALTER TABLE product_milestone_templates ADD COLUMN "reference" character varying(45); -UPDATE product_milestone_templates set reference='product' where reference is null; +UPDATE product_milestone_templates set reference='productTemplate' where reference is null; ALTER TABLE product_milestone_templates ALTER COLUMN "reference" SET NOT NULL; ALTER TABLE product_milestone_templates ADD COLUMN "metadata" json; diff --git a/postman.json b/postman.json index dda1b7a7..066fc82e 100644 --- a/postman.json +++ b/postman.json @@ -3970,7 +3970,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {}\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {}\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", @@ -4003,7 +4003,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1000,\r\n\t\"metadata\": {}\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 3\",\r\n \"description\": \"description 3\",\r\n \"duration\": 33,\r\n \"type\": \"type3\",\r\n \"order\": 1,\r\n \"activeText\": \"activeText 1\",\r\n \"completedText\": \"completedText 1\",\r\n \"blockedText\": \"blockedText 1\",\r\n \"plannedText\": \"planned Text 1\",\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1000,\r\n\t\"metadata\": {}\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates", @@ -4069,7 +4069,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"productTemplate\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"productTemplate\",\r\n \"referenceId\": 2\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", @@ -4103,7 +4103,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2000\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"productTemplate\",\r\n \"sourceReferenceId\": 1,\r\n \"reference\": \"productTemplate\",\r\n \"referenceId\": 2000\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", @@ -4137,7 +4137,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"product\",\r\n \"sourceReferenceId\": 1000,\r\n \"reference\": \"product\",\r\n \"referenceId\": 2\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"sourceReference\": \"productTemplate\",\r\n \"sourceReferenceId\": 1000,\r\n \"reference\": \"productTemplate\",\r\n \"referenceId\": 2\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/clone", @@ -4207,7 +4207,7 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3DproductTemplate%26referenceId%3D1", "host": [ "{{api-url}}" ], @@ -4220,7 +4220,7 @@ "query": [ { "key": "filter", - "value": "reference%3Dproduct%26referenceId%3D1" + "value": "reference%3DproductTemplate%26referenceId%3D1" } ] } @@ -4246,7 +4246,7 @@ "raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"key\":\"new key\",\r\n \"category\":\"new category\",\r\n \"scope\":{\r\n \"scope1\":\"scope 1\"\r\n },\r\n \"phases\":{\r\n \"phase1\":\"phase 1\"\r\n }\r\n }\r\n}" }, "url": { - "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1&sort=order desc", + "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates?filter=reference%3DproductTemplate%26referenceId%3D1&sort=order desc", "host": [ "{{api-url}}" ], @@ -4259,7 +4259,7 @@ "query": [ { "key": "filter", - "value": "reference%3Dproduct%26referenceId%3D1" + "value": "reference%3DproductTemplate%26referenceId%3D1" }, { "key": "sort", @@ -4320,7 +4320,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4354,7 +4354,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 2,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 2,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4388,7 +4388,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 1-updated\",\r\n\t\"description\": \"description 1-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type1-updated\",\r\n\t\"order\": 1,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4422,7 +4422,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 3,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 3,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4456,7 +4456,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 1,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestoneTemplate 1-updated\",\r\n \"description\": \"description 1-updated\",\r\n \"duration\": 34,\r\n \"type\": \"type1-updated\",\r\n \"order\": 1,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4490,7 +4490,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 5-updated\",\r\n\t\"description\": \"description 5-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type5-updated\",\r\n\t\"order\": 5,\r\n\t\"reference\": \"product\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {\r\n \"metadata1\": {\r\n \"name\": \"metadata 1 - update\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 1 - update\",\r\n \"newDetails\": \"new\"\r\n },\r\n \"others\": [\"others new\"]\r\n },\r\n \"metadata3\": {\r\n \"name\": \"metadata 3\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 3\"\r\n },\r\n \"others\": [\"others 31\", \"others 32\"]\r\n }\r\n }\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n\t\"name\": \"milestoneTemplate 5-updated\",\r\n\t\"description\": \"description 5-updated\",\r\n\t\"duration\": 34,\r\n\t\"type\": \"type5-updated\",\r\n\t\"order\": 5,\r\n\t\"reference\": \"productTemplate\",\r\n\t\"referenceId\": 1,\r\n\t\"metadata\": {\r\n \"metadata1\": {\r\n \"name\": \"metadata 1 - update\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 1 - update\",\r\n \"newDetails\": \"new\"\r\n },\r\n \"others\": [\"others new\"]\r\n },\r\n \"metadata3\": {\r\n \"name\": \"metadata 3\",\r\n \"details\": {\r\n \"anyDetails\": \"any details 3\"\r\n },\r\n \"others\": [\"others 31\", \"others 32\"]\r\n }\r\n }\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/metadata/milestoneTemplates/1", @@ -4543,6 +4543,31 @@ "response": [] } ] + }, + { + "name": "Metadata", + "item": [ + { + "name": "Get all metadata", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{api-url}}/v4/projects/metadata", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects", + "metadata" + ] + } + }, + "response": [] + } + ] } ] } \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index 1c76a97a..1ee789ad 100644 --- a/src/constants.js +++ b/src/constants.js @@ -115,7 +115,6 @@ export const TIMELINE_REFERENCES = { }; export const MILESTONE_TEMPLATE_REFERENCES = { - PROJECT: 'project', - PRODUCT: 'product', + PRODUCT_TEMPLATE: 'productTemplate', }; diff --git a/src/middlewares/validateMilestoneTemplate.js b/src/middlewares/validateMilestoneTemplate.js index 43c59c38..1aea3ace 100644 --- a/src/middlewares/validateMilestoneTemplate.js +++ b/src/middlewares/validateMilestoneTemplate.js @@ -10,26 +10,8 @@ import util from '../util'; * @returns {Promise} */ async function validateReference(sourceObject) { - // The source object refers to a project template - if (sourceObject.reference === MILESTONE_TEMPLATE_REFERENCES.PROJECT) { - // Validate projectTemplateId to be existed - const projectTemplate = await models.ProjectTemplate.findOne({ - where: { - id: sourceObject.referenceId, - deletedAt: { $eq: null }, - }, - }); - if (!projectTemplate) { - const apiErr = new Error( - `Project template not found for project id ${sourceObject.referenceId}`); - apiErr.status = 422; - throw apiErr; - } - return; - } - // The source object refers to a product template - if (sourceObject.reference === MILESTONE_TEMPLATE_REFERENCES.PRODUCT) { + if (sourceObject.reference === MILESTONE_TEMPLATE_REFERENCES.PRODUCT_TEMPLATE) { // Validate ProductTemplate to be existed const productTemplate = await models.ProductTemplate.findOne({ where: { @@ -39,7 +21,7 @@ async function validateReference(sourceObject) { }); if (!productTemplate) { const apiErr = new Error( - `Product template not found for product id ${sourceObject.referenceId}`); + `Product template not found for product template id ${sourceObject.referenceId}`); apiErr.status = 422; throw apiErr; } diff --git a/src/permissions/index.js b/src/permissions/index.js index f9b3668c..2bdfd10e 100644 --- a/src/permissions/index.js +++ b/src/permissions/index.js @@ -68,4 +68,6 @@ module.exports = () => { Authorizer.setPolicy('milestone.edit', projectEdit); Authorizer.setPolicy('milestone.delete', projectEdit); Authorizer.setPolicy('milestone.view', projectView); + + Authorizer.setPolicy('metadata.list', true); // anyone can view all metadata }; diff --git a/src/routes/index.js b/src/routes/index.js index 5b7a053d..ccfad599 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -46,6 +46,9 @@ router.route('/v4/projects/metadata/productCategories') router.route('/v4/projects/metadata/productCategories/:key') .get(require('./productCategories/get')); +router.route('/v4/projects/metadata') + .get(require('./metadata/list')); + router.all( RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), jwtAuth()); diff --git a/src/routes/metadata/list.js b/src/routes/metadata/list.js new file mode 100644 index 00000000..5a173913 --- /dev/null +++ b/src/routes/metadata/list.js @@ -0,0 +1,36 @@ +/** + * API to list all metadata + */ +import { middleware as tcMiddleware } from 'tc-core-library-js'; +import util from '../../util'; +import models from '../../models'; + +const permissions = tcMiddleware.permissions; + +module.exports = [ + permissions('metadata.list'), + (req, res, next) => { + const query = { + attributes: { exclude: ['deletedAt', 'deletedBy'] }, + raw: true, + }; + + return Promise.all([ + models.ProjectTemplate.findAll(query), + models.ProductTemplate.findAll(query), + models.MilestoneTemplate.findAll(query), + models.ProjectType.findAll(query), + models.ProductCategory.findAll(query), + ]) + .then((results) => { + res.json(util.wrapResponse(req.id, { + projectTemplates: results[0], + productTemplates: results[1], + milestoneTemplates: results[2], + projectTypes: results[3], + productCategories: results[4], + })); + }) + .catch(next); + }, +]; diff --git a/src/routes/metadata/list.spec.js b/src/routes/metadata/list.spec.js new file mode 100644 index 00000000..7e7c9b4f --- /dev/null +++ b/src/routes/metadata/list.spec.js @@ -0,0 +1,162 @@ +/** + * Tests for list.js + */ +import chai from 'chai'; +import request from 'supertest'; + +import models from '../../models'; +import server from '../../app'; +import testUtil from '../../tests/util'; + +const should = chai.should(); + +const projectTemplates = [ + { + name: 'template 1', + key: 'key 1', + category: 'category 1', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + scope: {}, + phases: {}, + createdBy: 1, + updatedBy: 1, + }, +]; +const productTemplates = [ + { + name: 'name 1', + productKey: 'productKey 1', + category: 'category', + icon: 'http://example.com/icon1.ico', + brief: 'brief 1', + details: 'details 1', + aliases: {}, + template: {}, + createdBy: 1, + updatedBy: 2, + }, +]; +const milestoneTemplates = [ + { + id: 1, + name: 'milestoneTemplate 1', + duration: 3, + type: 'type1', + order: 1, + plannedText: 'text to be shown in planned stage', + blockedText: 'text to be shown in blocked stage', + activeText: 'text to be shown in active stage', + completedText: 'text to be shown in completed stage', + reference: 'product', + referenceId: 1, + metadata: {}, + createdBy: 1, + updatedBy: 2, + }, +]; +const projectTypes = [ + { + key: 'key1', + displayName: 'displayName 1', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + metadata: { 'slack-notification-mappings': { color: '#96d957', label: 'Full App' } }, + createdBy: 1, + updatedBy: 1, + }, +]; +const productCategories = [ + { + key: 'key1', + displayName: 'displayName 1', + icon: 'http://example.com/icon1.ico', + question: 'question 1', + info: 'info 1', + aliases: ['key-1', 'key_1'], + createdBy: 1, + updatedBy: 1, + }, +]; + +describe('GET all metadata', () => { + beforeEach(() => 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)), + ); + after(testUtil.clearDb); + + describe('GET /projects/metadata', () => { + it('should return 200 even if user is not authenticated', (done) => { + request(server) + .get('/v4/projects/metadata') + .expect(200) + .end((err, res) => { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.projectTemplates.should.have.length(1); + resJson.productTemplates.should.have.length(1); + resJson.milestoneTemplates.should.have.length(1); + resJson.projectTypes.should.have.length(1); + resJson.productCategories.should.have.length(1); + + done(); + }); + }); + + it('should return 200 for admin', (done) => { + request(server) + .get('/v4/projects/metadata') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end(done); + }); + + it('should return 200 for admin', (done) => { + request(server) + .get('/v4/projects/metadata') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end(done); + }); + + it('should return 200 for connect manager', (done) => { + request(server) + .get('/v4/projects/metadata') + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .expect(200) + .end(done); + }); + + it('should return 200 for member', (done) => { + request(server) + .get('/v4/projects/metadata') + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .expect(200, done); + }); + + it('should return 200 for copilot', (done) => { + request(server) + .get('/v4/projects/metadata') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(200, done); + }); + }); +}); diff --git a/src/routes/milestoneTemplates/clone.spec.js b/src/routes/milestoneTemplates/clone.spec.js index edd0ec9e..03ad322a 100644 --- a/src/routes/milestoneTemplates/clone.spec.js +++ b/src/routes/milestoneTemplates/clone.spec.js @@ -83,7 +83,7 @@ const milestoneTemplates = [ duration: 3, type: 'type1', order: 1, - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, plannedText: 'text to be shown in planned stage', @@ -102,7 +102,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -120,9 +120,9 @@ describe('CLONE milestone template', () => { describe('POST /timelines/metadata/milestoneTemplates/clone', () => { const body = { param: { - sourceReference: 'product', + sourceReference: 'productTemplate', sourceReferenceId: 1, - reference: 'product', + reference: 'productTemplate', referenceId: 2, }, }; @@ -167,9 +167,9 @@ describe('CLONE milestone template', () => { it('should return 422 for non-existent product template', (done) => { const invalidBody = { param: { - sourceReference: 'product', + sourceReference: 'productTemplate', sourceReferenceId: 1, - reference: 'product', + reference: 'productTemplate', referenceId: 2000, }, }; @@ -206,7 +206,7 @@ describe('CLONE milestone template', () => { const invalidBody = { param: { sourceReferenceId: 1000, - reference: 'product', + reference: 'productTemplate', referenceId: 2, }, }; @@ -241,7 +241,7 @@ describe('CLONE milestone template', () => { resJson[0].blockedText.should.be.eql(milestoneTemplates[0].blockedText); resJson[0].activeText.should.be.eql(milestoneTemplates[0].activeText); resJson[0].completedText.should.be.eql(milestoneTemplates[0].completedText); - resJson[0].reference.should.be.eql('product'); + resJson[0].reference.should.be.eql('productTemplate'); resJson[0].referenceId.should.be.eql(2); resJson[0].metadata.should.be.eql({}); diff --git a/src/routes/milestoneTemplates/create.spec.js b/src/routes/milestoneTemplates/create.spec.js index ce9cd160..00dad710 100644 --- a/src/routes/milestoneTemplates/create.spec.js +++ b/src/routes/milestoneTemplates/create.spec.js @@ -64,7 +64,7 @@ const milestoneTemplates = [ duration: 3, type: 'type1', order: 1, - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, plannedText: 'text to be shown in planned stage', @@ -83,7 +83,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -111,7 +111,7 @@ describe('CREATE milestone template', () => { activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', hidden: true, - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, }, diff --git a/src/routes/milestoneTemplates/delete.spec.js b/src/routes/milestoneTemplates/delete.spec.js index c47db4dd..0d0d5527 100644 --- a/src/routes/milestoneTemplates/delete.spec.js +++ b/src/routes/milestoneTemplates/delete.spec.js @@ -94,7 +94,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 1, @@ -110,7 +110,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, diff --git a/src/routes/milestoneTemplates/get.spec.js b/src/routes/milestoneTemplates/get.spec.js index 39244fe4..58ce6a5a 100644 --- a/src/routes/milestoneTemplates/get.spec.js +++ b/src/routes/milestoneTemplates/get.spec.js @@ -69,7 +69,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 1, @@ -85,7 +85,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, diff --git a/src/routes/milestoneTemplates/list.spec.js b/src/routes/milestoneTemplates/list.spec.js index 29f8d2b7..2ee2f25f 100644 --- a/src/routes/milestoneTemplates/list.spec.js +++ b/src/routes/milestoneTemplates/list.spec.js @@ -69,7 +69,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 1, @@ -85,7 +85,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -101,7 +101,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 3', activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -178,7 +178,7 @@ describe('LIST milestone template', () => { it('should return 200 for connect admin with filter', (done) => { request(server) - .get('/v4/timelines/metadata/milestoneTemplates?filter=reference%3Dproduct%26referenceId%3D1') + .get('/v4/timelines/metadata/milestoneTemplates?filter=reference%3DproductTemplate%26referenceId%3D1') .set({ Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, }) diff --git a/src/routes/milestoneTemplates/update.js b/src/routes/milestoneTemplates/update.js index aea26df1..c9ce67e4 100644 --- a/src/routes/milestoneTemplates/update.js +++ b/src/routes/milestoneTemplates/update.js @@ -58,7 +58,7 @@ module.exports = [ let updated; // Merge JSON field - entityToUpdate.metadata = util.mergeJsonObjects(original.metadata, entityToUpdate.metadata || {}); + entityToUpdate.metadata = util.mergeJsonObjects(original.metadata, entityToUpdate.metadata || {}); return models.sequelize.transaction(() => // Update diff --git a/src/routes/milestoneTemplates/update.spec.js b/src/routes/milestoneTemplates/update.spec.js index 6091ae3c..68be6f31 100644 --- a/src/routes/milestoneTemplates/update.spec.js +++ b/src/routes/milestoneTemplates/update.spec.js @@ -69,7 +69,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage', activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 1, @@ -85,7 +85,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 2', activeText: 'text to be shown in active stage - 2', completedText: 'text to be shown in completed stage - 2', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -101,7 +101,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 3', activeText: 'text to be shown in active stage - 3', completedText: 'text to be shown in completed stage - 3', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -117,7 +117,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 4', activeText: 'text to be shown in active stage - 4', completedText: 'text to be shown in completed stage - 4', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, createdBy: 2, @@ -134,7 +134,7 @@ const milestoneTemplates = [ blockedText: 'text to be shown in blocked stage - 5', activeText: 'text to be shown in active stage - 5', completedText: 'text to be shown in completed stage - 5', - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: { metadata1: { @@ -153,8 +153,8 @@ const milestoneTemplates = [ }, }, createdBy: 2, - updatedBy: 3 - } + updatedBy: 3, + }, ]; describe('UPDATE milestone template', () => { @@ -177,7 +177,7 @@ describe('UPDATE milestone template', () => { activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', hidden: true, - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: {}, }, @@ -535,7 +535,7 @@ describe('UPDATE milestone template', () => { activeText: 'text to be shown in active stage', completedText: 'text to be shown in completed stage', hidden: true, - reference: 'product', + reference: 'productTemplate', referenceId: 1, metadata: { metadata1: { diff --git a/src/routes/timelines/create.js b/src/routes/timelines/create.js index fc8b2651..f7a5e3fc 100644 --- a/src/routes/timelines/create.js +++ b/src/routes/timelines/create.js @@ -62,7 +62,7 @@ module.exports = [ req.log.debug('Found templateId, finding milestone templates for the template'); return models.MilestoneTemplate.findAll({ where: { - reference: MILESTONE_TEMPLATE_REFERENCES.PRODUCT, + reference: MILESTONE_TEMPLATE_REFERENCES.PRODUCT_TEMPLATE, referenceId: templateId, deletedAt: { $eq: null }, }, diff --git a/src/tests/seed.js b/src/tests/seed.js index 8fbcd580..6b693e5c 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -447,8 +447,7 @@ models.sequelize.sync({ force: true }) duration: 3, type: 'type1', order: 1, - metadata: {}, - reference: 'product', + reference: 'productTemplate', referenceId: productTemplates[0].id, metadata: { metadata1: { @@ -479,7 +478,7 @@ models.sequelize.sync({ force: true }) type: 'type2', order: 2, metadata: {}, - reference: 'product', + reference: 'productTemplate', referenceId: productTemplates[0].id, activeText: 'activeText 2', completedText: 'completedText 2', diff --git a/swagger.yaml b/swagger.yaml index 9aafb6f1..781dfac5 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -647,6 +647,24 @@ paths: description: Project migrated successfully schema: $ref: "#/definitions/ProjectUpgradeResponse" + + /projects/metadata: + get: + tags: + - metadata + operationId: getAllMetadata + security: + - Bearer: [] + description: Retrieve all metadata including projectTemplates, productTemplates, milestoneTemplates, projectTypes, productCategories. All user roles can access this endpoint. + responses: + '200': + description: The metadata + schema: + $ref: "#/definitions/AllMetadataResponse" + '500': + description: Invalid server state or unknown error + schema: + $ref: "#/definitions/ErrorModel" /projects/metadata/projectTemplates: get: @@ -3454,8 +3472,7 @@ definitions: reference: type: string enum: - - product - - project + - productTemplate description: the milestone template reference refereneceId: type: number @@ -3487,8 +3504,7 @@ definitions: sourceReference: type: string enum: - - product - - project + - productTemplate description: the source reference to clone the milestone templates from sourceReferenceId: type: number @@ -3498,8 +3514,7 @@ definitions: reference: type: string enum: - - product - - project + - productTemplate description: the target reference to clone the milestone templates to refereneceId: type: number @@ -3588,3 +3603,48 @@ definitions: type: array items: $ref: "#/definitions/MilestoneTemplate" + + AllMetadataResponse: + title: All metadata response object + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: "#/definitions/ResponseMetadata" + content: + type: object + properties: + projectTemplates: + type: array + items: + $ref: "#/definitions/ProjectTemplate" + productTemplates: + type: array + items: + $ref: "#/definitions/ProductTemplate" + milestoneTemplates: + type: array + items: + $ref: "#/definitions/MilestoneTemplate" + projectTypes: + type: array + items: + $ref: "#/definitions/ProjectType" + productCategories: + type: array + items: + $ref: "#/definitions/ProductCategory" + \ No newline at end of file From 43c1a57eb591fb8ade17db428157d19c6d8acec5 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Mon, 27 Aug 2018 15:28:40 +0700 Subject: [PATCH 10/77] Add support for `order` in phases #168 --- migrations/20180827_project_phases_order.sql | 9 ++ postman.json | 113 +++++++++++++++- src/events/projectPhases/index.js | 15 ++- src/models/projectPhase.js | 1 + src/routes/phases/create.js | 51 +++++--- src/routes/phases/create.spec.js | 129 ++++++++++++------- src/routes/phases/list.js | 1 + src/routes/phases/update.js | 96 +++++++++++--- src/routes/phases/update.spec.js | 99 +++++++++----- swagger.yaml | 10 +- 10 files changed, 407 insertions(+), 117 deletions(-) create mode 100644 migrations/20180827_project_phases_order.sql diff --git a/migrations/20180827_project_phases_order.sql b/migrations/20180827_project_phases_order.sql new file mode 100644 index 00000000..4049c15a --- /dev/null +++ b/migrations/20180827_project_phases_order.sql @@ -0,0 +1,9 @@ +-- +-- UPDATE EXISTING TABLES: +-- project_phases: +-- added column `order` + +-- +-- project_phases +-- +ALTER TABLE project_phases ADD COLUMN "order" integer NULL; diff --git a/postman.json b/postman.json index 066fc82e..80cb269a 100644 --- a/postman.json +++ b/postman.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "6b341e19-8aa0-4827-8bdb-d676c3c5e20b", + "_postman_id": "607f173b-5427-4f94-8b56-8fbcab0ec7f0", "name": "tc-project-service", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -2027,6 +2027,39 @@ }, "response": [] }, + { + "name": "Create Phase with order", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase\",\n\t\t\"status\": \"active\",\n\t\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\t\"budget\": 20,\n\t\t\"details\": {\n\t\t\t\"aDetails\": \"a details\"\n\t\t},\n\t\t\"order\": 1\n\t}\n}" + }, + "url": { + "raw": "{{api-url}}/v4/projects/1/phases", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects", + "1", + "phases" + ] + } + }, + "response": [] + }, { "name": "List Phase", "request": { @@ -2138,6 +2171,45 @@ }, "response": [] }, + { + "name": "List Phase with sort by order", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase\",\n\t\t\"status\": \"active\",\n\t\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\t\"budget\": 20\n\t}\n}" + }, + "url": { + "raw": "{{api-url}}/v4/projects/1/phases?sort=order desc", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects", + "1", + "phases" + ], + "query": [ + { + "key": "sort", + "value": "order desc" + } + ] + } + }, + "response": [] + }, { "name": "Get Phase", "request": { @@ -2206,6 +2278,40 @@ }, "response": [] }, + { + "name": "Update Phase with order", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase xxx\",\n\t\t\"status\": \"inactive\",\n\t\t\"startDate\": \"2018-05-14T00:00:00\",\n\t\t\"endDate\": \"2018-05-15T00:00:00\",\n\t\t\"budget\": 30,\n\t\t\"progress\": 15,\n\t\t\"details\": {\n\t\t\t\"message\": \"phase details\"\n\t\t},\n\t\t\"order\": 1\n\t}\n}" + }, + "url": { + "raw": "{{api-url}}/v4/projects/1/phases/1", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects", + "1", + "phases", + "1" + ] + } + }, + "response": [] + }, { "name": "Delete Phase", "request": { @@ -4552,7 +4658,10 @@ "request": { "method": "GET", "header": [], - "body": {}, + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{api-url}}/v4/projects/metadata", "host": [ diff --git a/src/events/projectPhases/index.js b/src/events/projectPhases/index.js index 656dcfb1..09e964cb 100644 --- a/src/events/projectPhases/index.js +++ b/src/events/projectPhases/index.js @@ -29,6 +29,14 @@ const indexProjectPhase = Promise.coroutine(function* (logger, phase) { // eslin const existingPhaseIndex = _.findIndex(phases, p => p.id === phase.id); // if phase does not exists already if (existingPhaseIndex === -1) { + // Increase the order of the other phases in the same project, + // which have `order` >= this phase order + _.each(phases, (_phase) => { + if (!_.isNil(_phase.order) && !_.isNil(phase.order) && _phase.order >= phase.order) { + _phase.order += 1; // eslint-disable-line no-param-reassign + } + }); + phases.push(_.omit(phase, ['deletedAt', 'deletedBy'])); } else { // if phase already exists, ideally we should never land here, but code handles the buggy indexing // replaces the old inconsistent index where previously phase was not removed from the index but deleted @@ -109,12 +117,7 @@ const projectPhaseUpdatedHandler = 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, (single) => { // eslint-disable-line no-underscore-dangle - if (single.id === data.original.id) { - return _.assign(single, _.omit(data.updated, ['deletedAt', 'deletedBy'])); - } - return single; - }); + const phases = _.map(data.allPhases, single => _.omit(single, ['deletedAt', 'deletedBy'])); const merged = _.assign(doc._source, { phases }); // eslint-disable-line no-underscore-dangle yield eClient.update({ index: ES_PROJECT_INDEX, diff --git a/src/models/projectPhase.js b/src/models/projectPhase.js index 75b8e3dd..35649382 100644 --- a/src/models/projectPhase.js +++ b/src/models/projectPhase.js @@ -14,6 +14,7 @@ module.exports = function defineProjectPhase(sequelize, DataTypes) { spentBudget: { type: DataTypes.DOUBLE, defaultValue: 0.0 }, progress: { type: DataTypes.DOUBLE, defaultValue: 0.0 }, details: { type: DataTypes.JSON, defaultValue: {} }, + order: { type: DataTypes.INTEGER, allowNull: true }, deletedAt: { type: DataTypes.DATE, allowNull: true }, createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, diff --git a/src/routes/phases/create.js b/src/routes/phases/create.js index 3c5c2f83..95aee6e5 100644 --- a/src/routes/phases/create.js +++ b/src/routes/phases/create.js @@ -1,6 +1,7 @@ import validate from 'express-validation'; import _ from 'lodash'; import Joi from 'joi'; +import Sequelize from 'sequelize'; import models from '../../models'; import util from '../../util'; @@ -21,6 +22,7 @@ const addProjectPhaseValidations = { spentBudget: Joi.number().min(0).optional(), progress: Joi.number().min(0).optional(), details: Joi.any().optional(), + order: Joi.number().integer().optional(), }).required(), }, }; @@ -62,28 +64,45 @@ module.exports = [ .then((_newProjectPhase) => { newProjectPhase = _.cloneDeep(_newProjectPhase); req.log.debug('new project phase created (id# %d, name: %s)', - newProjectPhase.id, newProjectPhase.name); + newProjectPhase.id, newProjectPhase.name); newProjectPhase = newProjectPhase.get({ plain: true }); newProjectPhase = _.omit(newProjectPhase, ['deletedAt', 'deletedBy', 'utm']); }); - }); - }) - .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, - newProjectPhase, - { correlationId: req.id }, - ); - req.log.debug('Sending event to Kafka bus for project phase %d', newProjectPhase.id); - req.app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, { req, created: newProjectPhase }); + }) + .then(() => { + req.log.debug('re-ordering the other phases'); - res.status(201).json(util.wrapResponse(req.id, newProjectPhase, 1, 201)); + if (_.isNil(newProjectPhase.order)) { + return Promise.resolve(); + } + + // Increase the order of the other phases in the same project, + // which have `order` >= this phase order + return models.ProjectPhase.update({ order: Sequelize.literal('"order" + 1') }, { + where: { + projectId, + id: { $ne: newProjectPhase.id }, + order: { $gte: newProjectPhase.order }, + }, + }); + }); }) - .catch((err) => { - util.handleError('Error creating project phase', err, req, next); - }); + .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, + newProjectPhase, + { correlationId: req.id }, + ); + req.log.debug('Sending event to Kafka bus for project phase %d', newProjectPhase.id); + req.app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED, { req, created: newProjectPhase }); + + res.status(201).json(util.wrapResponse(req.id, newProjectPhase, 1, 201)); + }) + .catch((err) => { + util.handleError('Error creating project phase', err, req, next); + }); }, ]; diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index ff5de77e..faa02cdc 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -50,38 +50,38 @@ describe('Project Phases', () => { before((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, + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 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(() => done()); - }); + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => done()); }); + }); }); after((done) => { @@ -91,24 +91,24 @@ describe('Project Phases', () => { describe('POST /projects/{id}/phases/', () => { it('should return 403 if user does not have permissions (non team member)', (done) => { request(server) - .post(`/v4/projects/${projectId}/phases/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member2}`, - }) - .send({ param: body }) - .expect('Content-Type', /json/) - .expect(403, done); + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member2}`, + }) + .send({ param: body }) + .expect('Content-Type', /json/) + .expect(403, done); }); it('should return 403 if user does not have permissions (customer)', (done) => { request(server) - .post(`/v4/projects/${projectId}/phases/`) - .set({ - Authorization: `Bearer ${testUtil.jwts.member}`, - }) - .send({ param: body }) - .expect('Content-Type', /json/) - .expect(403, done); + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.member}`, + }) + .send({ param: body }) + .expect('Content-Type', /json/) + .expect(403, done); }); it('should return 422 when name not provided', (done) => { @@ -231,5 +231,48 @@ describe('Project Phases', () => { } }); }); + + it('should return 201 if payload has order specified', (done) => { + request(server) + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: _.assign({ order: 1 }, body) }) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + validatePhase(resJson, body); + resJson.order.should.be.eql(1); + + const firstPhaseId = resJson.id; + + // Create second phase + request(server) + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: _.assign({ order: 1 }, body) }) + .expect('Content-Type', /json/) + .expect(201) + .end((err2, res2) => { + const resJson2 = res2.body.result.content; + validatePhase(resJson2, body); + resJson2.order.should.be.eql(1); + + models.ProjectPhase.findOne({ where: { id: firstPhaseId } }) + .then((firstPhase) => { + firstPhase.order.should.be.eql(2); + done(); + }); + }); + } + }); + }); }); }); diff --git a/src/routes/phases/list.js b/src/routes/phases/list.js index 55d5ba63..e0084184 100644 --- a/src/routes/phases/list.js +++ b/src/routes/phases/list.js @@ -30,6 +30,7 @@ module.exports = [ 'startDate asc', 'startDate desc', 'endDate asc', 'endDate desc', 'status asc', 'status desc', + 'order asc', 'order desc', ]; if (sort && _.indexOf(sortableProps, sort) < 0) { return util.handleError('Invalid sort criteria', null, req, next); diff --git a/src/routes/phases/update.js b/src/routes/phases/update.js index 389157f2..1d9e4b53 100644 --- a/src/routes/phases/update.js +++ b/src/routes/phases/update.js @@ -2,6 +2,7 @@ import validate from 'express-validation'; import _ from 'lodash'; import Joi from 'joi'; +import Sequelize from 'sequelize'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import models from '../../models'; import util from '../../util'; @@ -22,6 +23,7 @@ const updateProjectPhaseValidation = { spentBudget: Joi.number().min(0).optional(), progress: Joi.number().min(0).optional(), details: Joi.any().optional(), + order: Joi.number().integer().optional(), }).required(), }, }; @@ -41,6 +43,7 @@ module.exports = [ updatedProps.updatedBy = req.authUser.userId; let previousValue; + let updated; models.sequelize.transaction(() => models.ProjectPhase.findOne({ where: { @@ -82,21 +85,82 @@ module.exports = [ existing.save().then(accept).catch(reject); } } - }))) - .then((updated) => { - req.log.debug('updated project phase', JSON.stringify(updated, null, 2)); - - // emit original and updated project phase information - req.app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, - { original: previousValue, updated }, - { correlationId: req.id }, - ); - req.app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, - { req, original: previousValue, updated }); - - res.json(util.wrapResponse(req.id, updated)); - }) - .catch(err => next(err)); + })) + .then((updatedPhase) => { + updated = updatedPhase; + + // Ignore re-ordering if there's no order specified for this phase + if (_.isNil(updated.order)) { + return Promise.resolve(); + } + + // Update order of the other phases only if the order was changed + if (previousValue.order === updated.order) { + return Promise.resolve(); + } + + return models.ProjectPhase.count({ + where: { + projectId, + id: { $ne: updated.id }, + order: updated.order, + }, + }) + .then((count) => { + if (count === 0) { + return Promise.resolve(); + } + + // Increase the order from M to K: if there is an item with order K, + // orders from M+1 to K should be made M to K-1 + if (!_.isNil(previousValue.order) && previousValue.order < updated.order) { + return models.ProjectPhase.update({ order: Sequelize.literal('"order" - 1') }, { + where: { + projectId, + id: { $ne: updated.id }, + order: { $between: [previousValue.order + 1, updated.order] }, + }, + }); + } + + // Decrease the order from M to K: if there is an item with order K, + // orders from K to M-1 should be made K+1 to M + return models.ProjectPhase.update({ order: Sequelize.literal('"order" + 1') }, { + where: { + projectId, + id: { $ne: updated.id }, + order: { + $between: [ + updated.order, + (previousValue.order ? previousValue.order : Number.MAX_SAFE_INTEGER) - 1, + ], + }, + }, + }); + }); + }) + .then(() => + // To simpify the logic, reload the phases from DB and send to the message queue + models.ProjectPhase.findAll({ + where: { + projectId, + }, + })), + ) + .then((allPhases) => { + req.log.debug('updated project phase', JSON.stringify(updated, null, 2)); + + // emit original and updated project phase information + req.app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, + { original: previousValue, updated, allPhases }, + { correlationId: req.id }, + ); + req.app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, + { req, original: previousValue, updated }); + + res.json(util.wrapResponse(req.id, updated)); + }) + .catch(err => next(err)); }, ]; diff --git a/src/routes/phases/update.spec.js b/src/routes/phases/update.spec.js index b154d339..ce9ddf16 100644 --- a/src/routes/phases/update.spec.js +++ b/src/routes/phases/update.spec.js @@ -46,6 +46,7 @@ const validatePhase = (resJson, expectedPhase) => { describe('Project Phases', () => { let projectId; let phaseId; + let phaseId2; const memberUser = { handle: testUtil.getDecodedToken(testUtil.jwts.member).handle, userId: testUtil.getDecodedToken(testUtil.jwts.member).userId, @@ -63,44 +64,51 @@ describe('Project Phases', () => { before((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, + }).then((p) => { + projectId = p.id; + // create members + models.ProjectMember.bulkCreate([{ + id: 1, + userId: copilotUser.userId, + projectId, + role: 'copilot', + isPrimary: false, createdBy: 1, updatedBy: 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; + }, { + id: 2, + userId: memberUser.userId, + projectId, + role: 'customer', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }]).then(() => { + _.assign(body, { projectId }); + const phases = [ + body, + _.assign({ order: 1 }, body), + ]; + models.ProjectPhase.bulkCreate(phases, { returning: true }) + .then((createdPhases) => { + phaseId = createdPhases[0].id; + phaseId2 = createdPhases[1].id; + done(); }); - }); }); }); + }); }); after((done) => { @@ -226,5 +234,32 @@ describe('Project Phases', () => { } }); }); + + it('should return updated phase if the order is specified', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: _.assign({ order: 1 }, updateBody) }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + validatePhase(resJson, updateBody); + resJson.order.should.be.eql(1); + + // Check the order of the other phase + models.ProjectPhase.findOne({ where: { id: phaseId2 } }) + .then((phase2) => { + phase2.order.should.be.eql(2); + done(); + }); + } + }); + }); }); }); diff --git a/swagger.yaml b/swagger.yaml index 781dfac5..098bd193 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -355,7 +355,7 @@ paths: - name: sort required: false description: | - sort project phases by startDate, endDate, status. Default is startDate asc + sort project phases by startDate, endDate, status, order. Default is startDate asc in: query type: string responses: @@ -373,7 +373,8 @@ paths: operationId: addProjectPhase security: - Bearer: [] - description: Create a project phase + description: Create a project phase. + It also updates the `order` field of all other phases in the same project which have `order` greater than or equal to the `order` specified in the POST body. parameters: - in: body name: body @@ -428,6 +429,7 @@ paths: security: - Bearer: [] description: Update a project phase. All users who can edit project can access this endpoint. + It also updates the `order` field of all other phases in the same project which have `order` greater than or equal to the `order` specified in the POST body. responses: '403': description: No permission or wrong token @@ -2634,6 +2636,10 @@ definitions: details: type: object description: the project phase details + order: + type: number + format: integer + description: the project phase order ProjectPhaseBodyParam: title: Project phase body param From affe4035eb5373f01c48256e5d604f28c78e5acd Mon Sep 17 00:00:00 2001 From: ngoctay Date: Mon, 27 Aug 2018 18:01:06 +0700 Subject: [PATCH 11/77] Add ability to create product in phase creation call #163 --- postman.json | 69 ++++++++++++++++++++++++++++- src/routes/phases/create.js | 75 +++++++++++++++++++++++--------- src/routes/phases/create.spec.js | 66 +++++++++++++++++++++++++++- src/routes/phases/update.js | 1 + swagger.yaml | 12 ++++- 5 files changed, 198 insertions(+), 25 deletions(-) diff --git a/postman.json b/postman.json index 80cb269a..ebc39ed8 100644 --- a/postman.json +++ b/postman.json @@ -2060,6 +2060,39 @@ }, "response": [] }, + { + "name": "Create Phase with productTemplateId", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase\",\n\t\t\"status\": \"active\",\n\t\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\t\"budget\": 20,\n\t\t\"details\": {\n\t\t\t\"aDetails\": \"a details\"\n\t\t},\n\t\t\"order\": 1,\n\t\t\"productTemplateId\": 1\n\t}\n}" + }, + "url": { + "raw": "{{api-url}}/v4/projects/1/phases", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects", + "1", + "phases" + ] + } + }, + "response": [] + }, { "name": "List Phase", "request": { @@ -3867,7 +3900,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 1-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-05-07T00:00:00.000Z\",\r\n \"status\": \"closed\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 1-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-09-28T00:00:00.000Z\",\r\n \"status\": \"closed\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/1/milestones/1", @@ -3885,6 +3918,40 @@ }, "response": [] }, + { + "name": "Update milestone 2", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 2-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-10-28T00:00:00.000Z\",\r\n \"status\": \"open\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines/1/milestones/2", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines", + "1", + "milestones", + "2" + ] + } + }, + "response": [] + }, { "name": "Update milestone (order 1 => 2)", "request": { diff --git a/src/routes/phases/create.js b/src/routes/phases/create.js index 95aee6e5..37a5e552 100644 --- a/src/routes/phases/create.js +++ b/src/routes/phases/create.js @@ -23,6 +23,7 @@ const addProjectPhaseValidations = { progress: Joi.number().min(0).optional(), details: Joi.any().optional(), order: Joi.number().integer().optional(), + productTemplateId: Joi.number().integer().positive().optional(), }).required(), }, }; @@ -48,28 +49,29 @@ module.exports = [ req.log.debug('Create Phase - Starting transaction'); return models.Project.findOne({ where: { id: projectId, deletedAt: { $eq: null } }, - }).then((existingProject) => { - if (!existingProject) { - const err = new Error(`active project not found for project id ${projectId}`); - err.status = 404; - throw err; - } - if (data.startDate !== null && data.endDate !== null && data.startDate > data.endDate) { - const err = new Error('startDate must not be after endDate.'); - err.status = 422; - throw err; - } - return models.ProjectPhase - .create(data) - .then((_newProjectPhase) => { - newProjectPhase = _.cloneDeep(_newProjectPhase); - req.log.debug('new project phase created (id# %d, name: %s)', - newProjectPhase.id, newProjectPhase.name); - - newProjectPhase = newProjectPhase.get({ plain: true }); - newProjectPhase = _.omit(newProjectPhase, ['deletedAt', 'deletedBy', 'utm']); - }); }) + .then((existingProject) => { + if (!existingProject) { + const err = new Error(`active project not found for project id ${projectId}`); + err.status = 404; + throw err; + } + if (data.startDate !== null && data.endDate !== null && data.startDate > data.endDate) { + const err = new Error('startDate must not be after endDate.'); + err.status = 422; + throw err; + } + return models.ProjectPhase + .create(data) + .then((_newProjectPhase) => { + newProjectPhase = _.cloneDeep(_newProjectPhase); + req.log.debug('new project phase created (id# %d, name: %s)', + newProjectPhase.id, newProjectPhase.name); + + newProjectPhase = newProjectPhase.get({ plain: true }); + newProjectPhase = _.omit(newProjectPhase, ['deletedAt', 'deletedBy', 'utm']); + }); + }) .then(() => { req.log.debug('re-ordering the other phases'); @@ -86,6 +88,37 @@ module.exports = [ order: { $gte: newProjectPhase.order }, }, }); + }) + .then(() => { + if (_.isNil(data.productTemplateId)) { + return Promise.resolve(); + } + + // Get the product template + return models.ProductTemplate.findById(data.productTemplateId) + .then((productTemplate) => { + if (!productTemplate) { + const err = new Error(`Product template does not exist with id = ${data.productTemplateId}`); + err.status = 422; + throw err; + } + + // Create the phase product + return models.PhaseProduct.create({ + name: productTemplate.name, + templateId: data.productTemplateId, + type: productTemplate.productKey, + projectId, + phaseId: newProjectPhase.id, + createdBy: req.authUser.userId, + updatedBy: req.authUser.userId, + }) + .then((phaseProduct) => { + newProjectPhase.products = [ + _.omit(phaseProduct.toJSON(), ['deletedAt', 'deletedBy']) + ]; + }); + }); }); }) .then(() => { diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index faa02cdc..8989c745 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -47,6 +47,7 @@ describe('Project Phases', () => { lastName: 'lName', email: 'some@abc.com', }; + let productTemplateId; before((done) => { // mocks testUtil.clearDb() @@ -79,9 +80,42 @@ describe('Project Phases', () => { isPrimary: true, createdBy: 1, updatedBy: 1, - }]).then(() => done()); + }]) }); - }); + }) + .then(() => + models.ProductTemplate.create({ + name: 'name 1', + productKey: 'productKey 1', + category: '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((template) => { + productTemplateId = template.id; + return Promise.resolve(); + }) + ) + .then(() => done()); }); after((done) => { @@ -274,5 +308,33 @@ describe('Project Phases', () => { } }); }); + + it('should return 201 if payload has productTemplateId specified', (done) => { + request(server) + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: _.assign({ productTemplateId }, body) }) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + validatePhase(resJson, body); + resJson.products.should.have.length(1); + + resJson.products[0].name.should.be.eql('name 1'); + resJson.products[0].templateId.should.be.eql(1); + resJson.products[0].type.should.be.eql('productKey 1'); + resJson.products[0].projectId.should.be.eql(1); + resJson.products[0].phaseId.should.be.eql(resJson.id); + + done(); + } + }); + }); }); }); diff --git a/src/routes/phases/update.js b/src/routes/phases/update.js index 1d9e4b53..7ec7717e 100644 --- a/src/routes/phases/update.js +++ b/src/routes/phases/update.js @@ -145,6 +145,7 @@ module.exports = [ where: { projectId, }, + include: [{ model: models.PhaseProduct, as: 'products' }] })), ) .then((allPhases) => { diff --git a/swagger.yaml b/swagger.yaml index 098bd193..8f081f9f 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -380,7 +380,17 @@ paths: name: body required: true schema: - $ref: '#/definitions/ProjectPhaseBodyParam' + type: object + allOf: + - $ref: '#/definitions/ProjectPhaseBodyParam' + properties: + param: + type: object + properties: + productTemplateId: + type: number + format: long + description: the optional productTemplateId used to populate a new phase product for the created phase responses: '403': description: No permission or wrong token From 44a15131593572a690660c735b95e556727aeb25 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Tue, 28 Aug 2018 01:28:06 +0700 Subject: [PATCH 12/77] New authorization scheme for project creation endpoint #171 --- src/middlewares/userIdAuth.js | 65 +++++++++++++++++++++++ src/routes/index.js | 16 +++++- src/routes/projects/create.spec.js | 85 ++++++++++++++++++++++++++++++ src/util.js | 12 +++-- 4 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 src/middlewares/userIdAuth.js diff --git a/src/middlewares/userIdAuth.js b/src/middlewares/userIdAuth.js new file mode 100644 index 00000000..e137c093 --- /dev/null +++ b/src/middlewares/userIdAuth.js @@ -0,0 +1,65 @@ +/** + * The userId authentication middleware. + */ +import config from 'config'; +import util from '../util'; + +const tcCoreLibAuth = require('tc-core-library-js').auth; + +const m2m = tcCoreLibAuth.m2m(config); + +/** + * The userId authentication middleware. + * @param {Object} req the request + * @param {Object} res the response + * @param {Function} next the next middleware + */ +module.exports = function userIdAuth(req, res, next) { + req.log.debug(`Enter userIdAuth middleware`); + + const bearerUserId = 'Bearer userId_'; + + if (!req.headers.authorization || + !req.headers.authorization.startsWith(bearerUserId) || + req.headers.authorization.length === bearerUserId.length) { + res.status(403) + .json(util.wrapErrorResponse(req.id, 403, 'No userId provided.')); + return res.send(); + } + + const userId = req.headers.authorization.split(bearerUserId)[1]; + + req.log.debug(`Get m2m token`); + + m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) + .then((token) => { + req.log.debug(`Get topcoder user from identity service, userId = ${userId}`); + + return util.getTopcoderUser(userId, token, req.log) + .then((user) => { + if (!user) { + res.status(403) + .json(util.wrapErrorResponse(req.id, 403, 'User does not exist.')); + return res.end(); + } + + if (user.active) { + res.status(403) + .json(util.wrapErrorResponse(req.id, 403, 'User is not inactive.')); + return res.end(); + } + + // Store user into the request + req.authUser = user; + req.authUser.userId = user.id; + req.authUser.roles = req.authUser.roles || []; + req.log.debug('req.authUser=>', req.authUser); + + return next(); + }); + }) + .catch((err) => { + req.log.error('Failed to get m2m token', err); + next(err); + }); +}; diff --git a/src/routes/index.js b/src/routes/index.js index ccfad599..5244586a 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -3,6 +3,7 @@ import _ from 'lodash'; import config from 'config'; import validate from 'express-validation'; import { Router } from 'express'; +import userIdAuth from '../middlewares/userIdAuth'; const router = Router(); @@ -50,7 +51,20 @@ router.route('/v4/projects/metadata') .get(require('./metadata/list')); router.all( - RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), jwtAuth()); + RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), (req, res, next) => { + // userId authentication for project creation endpoint + if (req.method === 'POST' && + req.path.endsWith('/v4/projects') && + req.headers.authorization && + req.headers.authorization.startsWith('Bearer userId_') + ) { + return userIdAuth(req, res, next); + } + + // JWT authentication + return jwtAuth()(req, res, next); + } +); // Register all the routes router.route('/v4/projects') diff --git a/src/routes/projects/create.spec.js b/src/routes/projects/create.spec.js index 7637cefe..967f5780 100644 --- a/src/routes/projects/create.spec.js +++ b/src/routes/projects/create.spec.js @@ -462,5 +462,90 @@ describe('Project create', () => { } }); }); + + it('should return 201 if valid user and data (using Bearer userId_)', (done) => { + const mockHttpClient = _.merge(testUtil.mockHttpClient, { + post: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: { + projectId: 128, + }, + }, + }, + }), + get: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: [ + { + id: 1800075, + active: false, + } + ], + }, + }, + }), + }); + sandbox.stub(util, 'getHttpClient', () => mockHttpClient); + request(server) + .post('/v4/projects') + .set({ + Authorization: `Bearer userId_1800075`, + }) + .send(_.merge({ param: { templateId: 3 } }, body)) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + server.log.error(err) + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + should.exist(resJson.billingAccountId); + should.exist(resJson.name); + resJson.directProjectId.should.be.eql(128); + resJson.status.should.be.eql('draft'); + resJson.type.should.be.eql(body.param.type); + resJson.members.should.have.lengthOf(1); + resJson.members[0].role.should.be.eql('customer'); + resJson.members[0].userId.should.be.eql(1800075); + resJson.members[0].projectId.should.be.eql(resJson.id); + resJson.members[0].isPrimary.should.be.truthy; + resJson.bookmarks.should.have.lengthOf(1); + resJson.bookmarks[0].title.should.be.eql('title1'); + resJson.bookmarks[0].address.should.be.eql('http://www.address.com'); + resJson.phases.should.have.lengthOf(3); + const phases = _.sortBy(resJson.phases, p => p.name); + phases[0].name.should.be.eql('Design Stage'); + phases[0].status.should.be.eql('open'); + phases[0].startDate.should.be.a('string'); + phases[0].duration.should.be.eql(10); + const startDate = moment.utc(phases[0].startDate); + startDate.hours().should.be.eql(0); + startDate.minutes().should.be.eql(0); + startDate.seconds().should.be.eql(0); + startDate.milliseconds().should.be.eql(0); + new Date(phases[0].endDate).should.be.eql(startDate.add(9, 'days').toDate()); + expect(phases[0].details).to.be.empty; + phases[0].products.should.have.lengthOf(1); + phases[0].products[0].name.should.be.eql('product 1'); + phases[0].products[0].templateId.should.be.eql(21); + server.services.pubsub.publish.calledWith('project.draft-created').should.be.true; + done(); + } + }); + }); }); }); diff --git a/src/util.js b/src/util.js index 79cecbc3..c699ccb9 100644 --- a/src/util.js +++ b/src/util.js @@ -297,10 +297,16 @@ _.assignIn(util, { httpClient.defaults.headers.common.Accept = 'application/json'; httpClient.defaults.headers.common['Content-Type'] = 'application/json'; httpClient.defaults.headers.common.Authorization = `Bearer ${jwtToken}`; - return httpClient.get(`${config.identityServiceEndpoint}users/${userId}`).then((response) => { + return httpClient.get(`${config.identityServiceEndpoint}users`, { + params: { + filter: `id=${userId}`, + }, + }) + .then((response) => { if (response.data && response.data.result - && response.data.result.status === 200 && response.data.result.content) { - return response.data.result.content; + && response.data.result.status === 200 && response.data.result.content + && response.data.result.content.length === 1) { + return response.data.result.content[0]; } return null; }); From 991f2f36d0a5690c8bddff743e8722d6a93b3408 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Tue, 28 Aug 2018 17:41:47 +0700 Subject: [PATCH 13/77] - New bus events for milestone notifications #172 - New authorization scheme for project creation endpoint #171 - Profiling of project service methods #157 --- config/custom-environment-variables.json | 3 +- config/default.json | 3 +- config/sample.local.js | 1 + config/test.json | 3 +- package.json | 1 + postman.json | 72 ++++++++++++++++++- postman_environment.json | 34 +++++---- src/app.js | 27 +++++++ src/constants.js | 7 ++ src/events/busApi.js | 91 +++++++++++++++++++++++- src/middlewares/userIdAuth.js | 18 ++++- src/routes/index.js | 2 +- src/routes/milestones/create.js | 17 +++-- src/routes/milestones/update.js | 3 + src/routes/phases/create.js | 2 +- src/routes/phases/create.spec.js | 4 +- src/routes/phases/update.js | 2 +- src/routes/projects/create.spec.js | 6 +- src/services/busApi.js | 2 +- src/util.js | 10 +-- 20 files changed, 259 insertions(+), 49 deletions(-) diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 3e0b0287..82606743 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -38,5 +38,6 @@ "AUTH0_CLIENT_ID": "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET": "AUTH0_CLIENT_SECRET", "AUTH0_AUDIENCE": "AUTH0_AUDIENCE", - "TOKEN_CACHE_TIME" : "TOKEN_CACHE_TIME" + "TOKEN_CACHE_TIME" : "TOKEN_CACHE_TIME", + "whitelistedOriginsForUserIdAuth": "WHITELISTED_ORIGINS_FOR_USERID_AUTH" } diff --git a/config/default.json b/config/default.json index 556a5a10..e2b2d11d 100644 --- a/config/default.json +++ b/config/default.json @@ -46,5 +46,6 @@ "AUTH0_CLIENT_SECRET": "", "AUTH0_AUDIENCE": "", "AUTH0_URL": "", - "TOKEN_CACHE_TIME": "" + "TOKEN_CACHE_TIME": "", + "whitelistedOriginsForUserIdAuth": "[\"https:\/\/topcoder-newauth.auth0.com\/\",\"https:\/\/api.topcoder-dev.com\"]" } diff --git a/config/sample.local.js b/config/sample.local.js index 058f9089..7a1b2a69 100644 --- a/config/sample.local.js +++ b/config/sample.local.js @@ -28,6 +28,7 @@ if (process.env.NODE_ENV === 'test') { indexName: 'projects', docType: 'projectV4' }, + whitelistedOriginsForUserIdAuth: "[\"\"]", }; } module.exports = config; diff --git a/config/test.json b/config/test.json index ab652133..23ca972e 100644 --- a/config/test.json +++ b/config/test.json @@ -18,5 +18,6 @@ "maxPoolSize": 50, "minPoolSize": 4, "idleTimeout": 1000 - } + }, + "whitelistedOriginsForUserIdAuth": "[\"\"]" } diff --git a/package.json b/package.json index 459d09cc..eab997b8 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "joi": "^8.0.5", "jsonwebtoken": "^8.3.0", "lodash": "^4.16.4", + "memwatch-next": "^0.3.0", "method-override": "^2.3.9", "moment": "^2.22.2", "pg": "^4.5.5", diff --git a/postman.json b/postman.json index ebc39ed8..8f6bdcc6 100644 --- a/postman.json +++ b/postman.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "607f173b-5427-4f94-8b56-8fbcab0ec7f0", + "_postman_id": "160fcac7-f74a-4047-a4e4-b53f08d991c5", "name": "tc-project-service", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -916,6 +916,38 @@ }, "response": [] }, + { + "name": "Create project by inactive user", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer userId_{{inactive-userId}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project\",\n\t\t\"description\": \"Hello I am a test project\",\n\t\t\"type\": \"generic\"\n\t}\n}" + }, + "url": { + "raw": "{{api-url}}/v4/projects", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "projects" + ] + }, + "description": "Valid request body. Project should be created successfully." + }, + "response": [] + }, { "name": "Get project by id", "request": { @@ -3919,7 +3951,41 @@ "response": [] }, { - "name": "Update milestone 2", + "name": "Update milestone (active)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt-token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 2-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-10-28T00:00:00.000Z\",\r\n \"status\": \"active\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" + }, + "url": { + "raw": "{{api-url}}/v4/timelines/1/milestones/2", + "host": [ + "{{api-url}}" + ], + "path": [ + "v4", + "timelines", + "1", + "milestones", + "2" + ] + } + }, + "response": [] + }, + { + "name": "Update milestone (completed)", "request": { "method": "PATCH", "header": [ @@ -3934,7 +4000,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 2-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-10-28T00:00:00.000Z\",\r\n \"status\": \"open\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" + "raw": "{\r\n \"param\":{\r\n \"name\": \"milestone 2-updated\",\r\n \"description\": \"description-updated\",\r\n \"duration\": 3,\r\n \"completionDate\": \"2018-10-28T00:00:00.000Z\",\r\n \"status\": \"completed\",\r\n \"type\": \"type2\",\r\n \"details\": {\r\n \"detail1\": {\r\n \"subDetail1C\": 3\r\n },\r\n \"detail2\": [\r\n 4\r\n ]\r\n },\r\n \"order\": 1,\r\n \"plannedText\": \"plannedText 1-updated\",\r\n \"activeText\": \"activeText 1-updated\",\r\n \"completedText\": \"completedText 1-updated\",\r\n \"blockedText\": \"blockedText 1-updated\"\r\n }\r\n}" }, "url": { "raw": "{{api-url}}/v4/timelines/1/milestones/2", diff --git a/postman_environment.json b/postman_environment.json index d1ccbbd4..261834ae 100644 --- a/postman_environment.json +++ b/postman_environment.json @@ -1,65 +1,63 @@ { - "id": "e6b30b4b-1388-4622-8314-bc49ba1d752b", + "id": "53925cd5-ff42-43a2-bb87-29f9aa73ffd9", "name": "tc-project-service", "values": [ { "key": "api-url", "value": "http://localhost:3000", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-admin-40051333", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-member-40051331", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzEiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.pDtRzcGQjgCBD6aLsW-1OFhzmrv5mXhb8YLDWbGAnKo", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-copilot-40051332", - "value": "", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBDb3BpbG90Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjo0MDA1MTMzMiwiZW1haWwiOiJ0ZXN0QHRvcGNvZGVyLmNvbSIsImlhdCI6MTQ3MDYyMDA0NH0.DnX17gBaVF2JTuRai-C2BDSdEjij9da_s4eYcMIjP0c", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-manager-40051334", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29ubmVjdCBNYW5hZ2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzQiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.J5VtOEQVph5jfe2Ji-NH7txEDcx_5gthhFeD-MzX9ck", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-member2-40051335", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJtZW1iZXIyIiwiZXhwIjoyNTYzMDc2Njg5LCJ1c2VySWQiOiI0MDA1MTMzNSIsImlhdCI6MTQ2MzA3NjA4OSwiZW1haWwiOiJ0ZXN0QHRvcGNvZGVyLmNvbSIsImp0aSI6ImIzM2I3N2NkLWI1MmUtNDBmZS04MzdlLWJlYjhlMGFlNmE0YSJ9.Mh4bw3wm-cn5Kcf96gLFVlD0kySOqqk4xN3qnreAKL4", "description": "", - "type": "text", "enabled": true }, { "key": "jwt-token-connectAdmin-40051336", - "value": "", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJDb25uZWN0IEFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJjb25uZWN0X2FkbWluMSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzYiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoiY29ubmVjdF9hZG1pbjFAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.nSGfXMl02NZ90ZKLiEKPg75iAjU92mfteaY6xgqkM30", + "description": "", + "enabled": true + }, + { + "key": "inactive-userId", + "value": "1800075", "description": "", - "type": "text", "enabled": true } ], "_postman_variable_scope": "environment", - "_postman_exported_at": "2018-05-18T18:54:18.167Z", - "_postman_exported_using": "Postman/6.0.10" + "_postman_exported_at": "2018-08-28T10:28:37.251Z", + "_postman_exported_using": "Postman/6.2.5" } \ No newline at end of file diff --git a/src/app.js b/src/app.js index 864fb0e6..bd02c11c 100644 --- a/src/app.js +++ b/src/app.js @@ -7,6 +7,7 @@ import config from 'config'; import cors from 'cors'; import coreLib from 'tc-core-library-js'; import expressRequestId from 'express-request-id'; +import memWatch from 'memwatch-next'; import router from './routes'; import permissions from './permissions'; import models from './models'; @@ -64,6 +65,32 @@ const logger = coreLib.logger({ app.use(coreLib.middleware.logger(null, logger)); app.logger = logger; +// ======================== +// Memory leak detection +// ======================== +if (process.env.NODE_ENV.toLowerCase() === 'development') { + let heapDiff = null; + + // A leak event will be emitted when the heap usage has increased + // for five consecutive garbage collections + memWatch.on('leak', (info) => { + logger.error('memwatch::leak=>', info); + + if (!heapDiff) { + heapDiff = new memWatch.HeapDiff(); + } else { + const diff = heapDiff.end(); + logger.error('memwatch::diff=>', diff); + heapDiff = null; + } + }); + + // When V8 performs a garbage collection, memwatch will emit a stats event + memWatch.on('stats', (stats) => { + logger.debug('memwatch::stats=>', stats); + }); +} + // ======================= // CORS ================ // ======================= diff --git a/src/constants.js b/src/constants.js index 1ee789ad..90248b1f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -98,6 +98,13 @@ export const BUS_API_EVENT = { // When specification of a product is modified PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'notifications.connect.project.productSpecificationModified', + + // When milestone is marked as active + MILESTONE_TRANSITION_ACTIVE: 'notifications.connect.project.phase.milestone.transition.active', + // When milestone is marked as completed + MILESTONE_TRANSITION_COMPLETED: 'notifications.connect.project.phase.milestone.transition.completed', + // When milestone is waiting for customers's input + MILESTONE_WAITING_CUSTOMER: 'notifications.connect.project.phase.milestone.waiting.customer', }; export const REGEX = { diff --git a/src/events/busApi.js b/src/events/busApi.js index c10613bc..3ad4dd5e 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -1,6 +1,7 @@ import _ from 'lodash'; import config from 'config'; -import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE } from '../constants'; +import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE, MILESTONE_STATUS } + from '../constants'; import { createEvent } from '../services/busApi'; import models from '../models'; @@ -414,4 +415,92 @@ module.exports = (app, logger) => { } }).catch(err => null); // eslint-disable-line no-unused-vars }); + + /** + * Send milestone notification if needed. + * @param {Object} req the request + * @param {Object} original the original milestone + * @param {Object} updated the updated milestone + * @param {Object} project the project + * @returns {Promise} void + */ + function sendMilestoneNotification(req, original, updated, project) { + // Send transition events + if (original.status !== updated.status) { + let event; + if (updated.status === MILESTONE_STATUS.COMPLETED) { + event = BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED; + } else if (updated.status === MILESTONE_STATUS.ACTIVE) { + event = BUS_API_EVENT.MILESTONE_TRANSITION_ACTIVE; + } + + if (event) { + createEvent(event, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + timelineId: req.timeline.id, + timelineName: req.timeline.name, + milestoneId: updated.id, + milestoneName: updated.name, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + } + + // Send notifications.connect.project.phase.milestone.waiting.customer event + const originalWaiting = _.get(original, 'details.metadata.waitingForCustomer', false); + const updatedWaiting = _.get(updated, 'details.metadata.waitingForCustomer', false); + if (!originalWaiting && updatedWaiting) { + createEvent(BUS_API_EVENT.MILESTONE_WAITING_CUSTOMER, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + timelineId: req.timeline.id, + timelineName: req.timeline.name, + milestoneId: updated.id, + milestoneName: updated.name, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + } + + /** + * MILESTONE_ADDED. + */ + app.on(EVENT.ROUTING_KEY.MILESTONE_ADDED, ({ req, created }) => { + logger.debug('receive MILESTONE_ADDED event'); + + const projectId = _.parseInt(req.params.projectId); + + models.Project.findOne({ + where: { id: projectId }, + }) + .then(project => sendMilestoneNotification(req, {}, created, project)) + .catch(err => null); // eslint-disable-line no-unused-vars + }); + + /** + * MILESTONE_UPDATED. + */ + // eslint-disable-next-line no-unused-vars + app.on(EVENT.ROUTING_KEY.MILESTONE_UPDATED, ({ req, original, updated, cascadedUpdates }) => { + logger.debug('receive MILESTONE_UPDATED event'); + + const projectId = _.parseInt(req.params.projectId); + + models.Project.findOne({ + where: { id: projectId }, + }) + .then((project) => { + sendMilestoneNotification(req, original, updated, project); + + _.each(cascadedUpdates, cascadedUpdate => + sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project), + ); + }) + .catch(err => null); // eslint-disable-line no-unused-vars + }); }; diff --git a/src/middlewares/userIdAuth.js b/src/middlewares/userIdAuth.js index e137c093..093da759 100644 --- a/src/middlewares/userIdAuth.js +++ b/src/middlewares/userIdAuth.js @@ -2,20 +2,24 @@ * The userId authentication middleware. */ import config from 'config'; +import _ from 'lodash'; import util from '../util'; const tcCoreLibAuth = require('tc-core-library-js').auth; const m2m = tcCoreLibAuth.m2m(config); +const whitelistedOrigins = JSON.parse(config.get('whitelistedOriginsForUserIdAuth')); + /** * The userId authentication middleware. * @param {Object} req the request * @param {Object} res the response * @param {Function} next the next middleware + * @returns {Promise} void */ -module.exports = function userIdAuth(req, res, next) { - req.log.debug(`Enter userIdAuth middleware`); +module.exports = function userIdAuth(req, res, next) { // eslint-disable-line consistent-return + req.log.debug('Enter userIdAuth middleware'); const bearerUserId = 'Bearer userId_'; @@ -27,9 +31,17 @@ module.exports = function userIdAuth(req, res, next) { return res.send(); } + // Check origin + const origin = req.header('Origin') || ' '; + if (!_.some(whitelistedOrigins, whitelistedOrigin => origin.startsWith(whitelistedOrigin))) { + res.status(403).json( + util.wrapErrorResponse(req.id, 403, `Origin ${origin} is not allowed to access this authentication scheme`)); + return res.end(); + } + const userId = req.headers.authorization.split(bearerUserId)[1]; - req.log.debug(`Get m2m token`); + req.log.debug('Get m2m token'); m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) .then((token) => { diff --git a/src/routes/index.js b/src/routes/index.js index 5244586a..c2a39ec8 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -63,7 +63,7 @@ router.all( // JWT authentication return jwtAuth()(req, res, next); - } + }, ); // Register all the routes diff --git a/src/routes/milestones/create.js b/src/routes/milestones/create.js index e0643d38..eadec1f4 100644 --- a/src/routes/milestones/create.js +++ b/src/routes/milestones/create.js @@ -80,13 +80,6 @@ module.exports = [ // Omit deletedAt, deletedBy result = _.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy'); - // 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 }, - ); - // Increase the order of the other milestones in the same timeline, // which have `order` >= this milestone order return models.Milestone.update({ order: Sequelize.literal('"order" + 1') }, { @@ -104,6 +97,16 @@ module.exports = [ // because it will make 'version conflict' error in ES. // The order of the other milestones need to be updated in the MILESTONE_ADDED event handler + // 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.app.emit(EVENT.ROUTING_KEY.MILESTONE_ADDED, + { req, created: result }); + // Write to the response res.status(201).json(util.wrapResponse(req.id, result, 1, 201)); return Promise.resolve(); diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index 24230354..cbb2f2a3 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -284,6 +284,9 @@ module.exports = [ // because it will make 'version conflict' error in ES. // The order of the other milestones need to be updated in the MILESTONE_UPDATED event above + req.app.emit(EVENT.ROUTING_KEY.MILESTONE_UPDATED, + { req, original, updated, cascadedUpdates }); + // Write to response res.json(util.wrapResponse(req.id, updated)); return Promise.resolve(); diff --git a/src/routes/phases/create.js b/src/routes/phases/create.js index 37a5e552..b4da22a1 100644 --- a/src/routes/phases/create.js +++ b/src/routes/phases/create.js @@ -115,7 +115,7 @@ module.exports = [ }) .then((phaseProduct) => { newProjectPhase.products = [ - _.omit(phaseProduct.toJSON(), ['deletedAt', 'deletedBy']) + _.omit(phaseProduct.toJSON(), ['deletedAt', 'deletedBy']), ]; }); }); diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index 8989c745..2f84160f 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -80,7 +80,7 @@ describe('Project Phases', () => { isPrimary: true, createdBy: 1, updatedBy: 1, - }]) + }]); }); }) .then(() => @@ -113,7 +113,7 @@ describe('Project Phases', () => { }).then((template) => { productTemplateId = template.id; return Promise.resolve(); - }) + }), ) .then(() => done()); }); diff --git a/src/routes/phases/update.js b/src/routes/phases/update.js index 7ec7717e..d2c6a9b4 100644 --- a/src/routes/phases/update.js +++ b/src/routes/phases/update.js @@ -145,7 +145,7 @@ module.exports = [ where: { projectId, }, - include: [{ model: models.PhaseProduct, as: 'products' }] + include: [{ model: models.PhaseProduct, as: 'products' }], })), ) .then((allPhases) => { diff --git a/src/routes/projects/create.spec.js b/src/routes/projects/create.spec.js index 967f5780..dcec4e55 100644 --- a/src/routes/projects/create.spec.js +++ b/src/routes/projects/create.spec.js @@ -491,7 +491,7 @@ describe('Project create', () => { { id: 1800075, active: false, - } + }, ], }, }, @@ -501,14 +501,14 @@ describe('Project create', () => { request(server) .post('/v4/projects') .set({ - Authorization: `Bearer userId_1800075`, + Authorization: 'Bearer userId_1800075', }) .send(_.merge({ param: { templateId: 3 } }, body)) .expect('Content-Type', /json/) .expect(201) .end((err, res) => { if (err) { - server.log.error(err) + server.log.error(err); done(err); } else { const resJson = res.body.result.content; diff --git a/src/services/busApi.js b/src/services/busApi.js index 5d3aa07d..ddc58656 100644 --- a/src/services/busApi.js +++ b/src/services/busApi.js @@ -43,7 +43,7 @@ async function getClient() { * @return {Promise} new event promise */ function createEvent(topic, payload, logger) { - logger.debug(`Sending message: ${JSON.stringify(payload)}`); + logger.debug(`Sending message to topic ${topic}: ${JSON.stringify(payload)}`); return getClient().then((busClient) => { logger.debug('calling bus-api'); return busClient.post('/bus/events', { diff --git a/src/util.js b/src/util.js index c699ccb9..10e17fb6 100644 --- a/src/util.js +++ b/src/util.js @@ -303,13 +303,13 @@ _.assignIn(util, { }, }) .then((response) => { - if (response.data && response.data.result + if (response.data && response.data.result && response.data.result.status === 200 && response.data.result.content && response.data.result.content.length === 1) { - return response.data.result.content[0]; - } - return null; - }); + return response.data.result.content[0]; + } + return null; + }); }, /** From 303d032f0e2690a6d2fdc31abc9e127993322f73 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Wed, 29 Aug 2018 09:30:27 +0700 Subject: [PATCH 14/77] - Send original and updated milestones in the milestone events to BusAPI - Move the code to get M2M token to util.js#getM2MToken() --- src/events/busApi.js | 8 ++++---- src/middlewares/userIdAuth.js | 7 +------ src/util.js | 10 ++++++++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 3ad4dd5e..7e5167bf 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -441,8 +441,8 @@ module.exports = (app, logger) => { projectUrl: connectProjectUrl(project.id), timelineId: req.timeline.id, timelineName: req.timeline.name, - milestoneId: updated.id, - milestoneName: updated.name, + originalMilestone: original, + updatedMilestone: updated, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); @@ -459,8 +459,8 @@ module.exports = (app, logger) => { projectUrl: connectProjectUrl(project.id), timelineId: req.timeline.id, timelineName: req.timeline.name, - milestoneId: updated.id, - milestoneName: updated.name, + originalMilestone: original, + updatedMilestone: updated, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); diff --git a/src/middlewares/userIdAuth.js b/src/middlewares/userIdAuth.js index 093da759..20f90c9d 100644 --- a/src/middlewares/userIdAuth.js +++ b/src/middlewares/userIdAuth.js @@ -5,10 +5,6 @@ import config from 'config'; import _ from 'lodash'; import util from '../util'; -const tcCoreLibAuth = require('tc-core-library-js').auth; - -const m2m = tcCoreLibAuth.m2m(config); - const whitelistedOrigins = JSON.parse(config.get('whitelistedOriginsForUserIdAuth')); /** @@ -42,8 +38,7 @@ module.exports = function userIdAuth(req, res, next) { // eslint-disable-line co const userId = req.headers.authorization.split(bearerUserId)[1]; req.log.debug('Get m2m token'); - - m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) + util.getM2MToken() .then((token) => { req.log.debug(`Get topcoder user from identity service, userId = ${userId}`); diff --git a/src/util.js b/src/util.js index 10e17fb6..34e12c7d 100644 --- a/src/util.js +++ b/src/util.js @@ -17,10 +17,14 @@ import urlencode from 'urlencode'; import elasticsearch from 'elasticsearch'; import Promise from 'bluebird'; import AWS from 'aws-sdk'; + import { ADMIN_ROLES, TOKEN_SCOPES } from './constants'; const exec = require('child_process').exec; const models = require('./models').default; +const tcCoreLibAuth = require('tc-core-library-js').auth; + +const m2m = tcCoreLibAuth.m2m(config); const util = _.cloneDeep(require('tc-core-library-js').util(config)); @@ -282,6 +286,12 @@ _.assignIn(util, { .then(res => res.data.result.content.token); }, + /** + * Get machine to machine token. + * @returns {Promise} promise which resolves to the m2m token + */ + getM2MToken: () => m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET), + /** * Fetches the topcoder user details using the given JWT token. * From 105095160b762d272deaa16f76371e534ca4fa75 Mon Sep 17 00:00:00 2001 From: ngoctay Date: Thu, 30 Aug 2018 09:02:54 +0700 Subject: [PATCH 15/77] Added performance logging middleware --- package-lock.json | 3407 +++++++++++-------- src/app.js | 3 +- src/middlewares/performanceRequestLogger.js | 37 + 3 files changed, 2016 insertions(+), 1431 deletions(-) create mode 100644 src/middlewares/performanceRequestLogger.js diff --git a/package-lock.json b/package-lock.json index 88934335..ccc64298 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-1.1.2.tgz", "integrity": "sha1-13hAmZ4/fkPnSzsNQzkcFSb3k7g=", "requires": { - "component-type": "1.2.1", - "join-component": "1.1.0" + "component-type": "^1.2.1", + "join-component": "^1.1.0" } }, "@types/body-parser": { @@ -18,8 +18,8 @@ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.16.8.tgz", "integrity": "sha512-BdN2PXxOFnTXFcyONPW6t0fHjz2fvRZHVMFpaS0wYr+Y8fWEaNOs4V8LEu/fpzQlMx+ahdndgTaGTwPC+J/EeA==", "requires": { - "@types/express": "4.0.39", - "@types/node": "8.5.1" + "@types/express": "*", + "@types/node": "*" } }, "@types/express": { @@ -27,9 +27,9 @@ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.39.tgz", "integrity": "sha512-dBUam7jEjyuEofigUXCtublUHknRZvcRgITlGsTbFgPvnTwtQUt2NgLakbsf+PsGo/Nupqr3IXCYsOpBpofyrA==", "requires": { - "@types/body-parser": "1.16.8", - "@types/express-serve-static-core": "4.0.57", - "@types/serve-static": "1.13.1" + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" } }, "@types/express-jwt": { @@ -37,8 +37,8 @@ "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.34.tgz", "integrity": "sha1-/b7kxq9cCiRu8qkz9VGZc8dxfwI=", "requires": { - "@types/express": "4.0.39", - "@types/express-unless": "0.0.32" + "@types/express": "*", + "@types/express-unless": "*" } }, "@types/express-serve-static-core": { @@ -46,7 +46,7 @@ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.0.57.tgz", "integrity": "sha512-QLAHjdLwEICm3thVbXSKRoisjfgMVI4xJH/HU8F385BR2HI7PmM6ax4ELXf8Du6sLmSpySXMYaI+xc//oQ/IFw==", "requires": { - "@types/node": "8.5.1" + "@types/node": "*" } }, "@types/express-unless": { @@ -54,7 +54,7 @@ "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.0.32.tgz", "integrity": "sha512-6YpJyFNlDDnPnRjMOvJCoDYlSDDmG/OEEUsPk7yhNkL4G9hUYtgab6vi1CcWsGSSSM0CsvNlWTG+ywAGnvF03g==", "requires": { - "@types/express": "4.0.39" + "@types/express": "*" } }, "@types/geojson": { @@ -77,8 +77,8 @@ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.1.tgz", "integrity": "sha512-jDMH+3BQPtvqZVIcsH700Dfi8Q3MIcEx16g/VdxjoqiGR/NntekB10xdBpirMKnPe9z2C5cBmL0vte0YttOr3Q==", "requires": { - "@types/express-serve-static-core": "4.0.57", - "@types/mime": "2.0.0" + "@types/express-serve-static-core": "*", + "@types/mime": "*" } }, "abbrev": { @@ -92,7 +92,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", "requires": { - "mime-types": "2.1.17", + "mime-types": "~2.1.16", "negotiator": "0.6.1" } }, @@ -108,7 +108,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "3.3.0" + "acorn": "^3.0.4" }, "dependencies": { "acorn": { @@ -125,8 +125,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "ajv-keywords": { @@ -141,9 +141,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "amdefine": { @@ -157,10 +157,10 @@ "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.1.tgz", "integrity": "sha1-fMz+ur5WwumE6noiQ/fO/m+/xs8=", "requires": { - "bitsyntax": "0.0.4", - "bluebird": "3.5.1", + "bitsyntax": "~0.0.4", + "bluebird": "^3.4.6", "buffer-more-ints": "0.0.2", - "readable-stream": "1.1.14" + "readable-stream": "1.x >=1.1.9" } }, "analytics-node": { @@ -168,15 +168,15 @@ "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-2.4.1.tgz", "integrity": "sha1-H5bI64h7bEdpEESsf8mhIx+wIPc=", "requires": { - "@segment/loosely-validate-event": "1.1.2", - "clone": "2.1.1", - "commander": "2.11.0", - "crypto-token": "1.0.1", - "debug": "2.6.9", - "lodash": "4.17.4", - "remove-trailing-slash": "0.1.0", - "superagent": "3.8.0", - "superagent-retry": "0.6.0" + "@segment/loosely-validate-event": "^1.1.2", + "clone": "^2.1.1", + "commander": "^2.9.0", + "crypto-token": "^1.0.1", + "debug": "^2.6.2", + "lodash": "^4.17.4", + "remove-trailing-slash": "^0.1.0", + "superagent": "^3.5.0", + "superagent-retry": "^0.6.0" } }, "ansi-align": { @@ -185,7 +185,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -206,8 +206,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -216,7 +216,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -243,8 +243,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "app-module-path": { @@ -258,7 +258,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "1.0.0" + "default-require-extensions": "^1.0.0" } }, "archy": { @@ -273,7 +273,7 @@ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -282,7 +282,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "arr-flatten": { @@ -320,7 +320,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -379,8 +379,8 @@ "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.8.tgz", "integrity": "sha512-1Sy1jDhjlgxcSd9/ICHqiAHT8VSJ9R1lzEyWwP/4Hm9p8nVTNtU0SxG/Z15XHD/aZvQraSw9BpDU3EBcFnOVrw==", "requires": { - "semver": "5.4.1", - "shimmer": "1.1.0" + "semver": "^5.3.0", + "shimmer": "^1.1.0" } }, "asynckit": { @@ -393,13 +393,13 @@ "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.6.0.tgz", "integrity": "sha1-2a4wFIBzZtO0ecKtGKNTfz4Mlpk=", "requires": { - "base64-js": "1.2.1", - "idtoken-verifier": "1.2.0", - "js-cookie": "2.2.0", - "qs": "6.5.1", - "superagent": "3.8.3", - "url-join": "1.1.0", - "winchan": "0.2.0" + "base64-js": "^1.2.0", + "idtoken-verifier": "^1.2.0", + "js-cookie": "^2.2.0", + "qs": "^6.4.0", + "superagent": "^3.8.2", + "url-join": "^1.1.0", + "winchan": "^0.2.0" }, "dependencies": { "debug": { @@ -430,13 +430,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -444,7 +444,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "superagent": { @@ -452,16 +452,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "3.1.0", - "extend": "3.0.1", - "form-data": "2.3.1", - "formidable": "1.2.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.6" + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" } } } @@ -473,7 +473,7 @@ "requires": { "buffer": "4.9.1", "crypto-browserify": "1.0.9", - "events": "1.1.1", + "events": "^1.1.1", "jmespath": "0.15.0", "querystring": "0.2.0", "sax": "1.2.1", @@ -498,8 +498,8 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", "requires": { - "follow-redirects": "1.2.6", - "is-buffer": "1.1.6" + "follow-redirects": "^1.2.5", + "is-buffer": "^1.1.5" } }, "babel-cli": { @@ -508,21 +508,21 @@ "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-polyfill": "6.26.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "chokidar": "1.7.0", - "commander": "2.11.0", - "convert-source-map": "1.5.0", - "fs-readdir-recursive": "1.0.0", - "glob": "7.1.2", - "lodash": "4.17.4", - "output-file-sync": "1.1.2", - "path-is-absolute": "1.0.1", - "slash": "1.0.0", - "source-map": "0.5.7", - "v8flags": "2.1.1" + "babel-core": "^6.26.0", + "babel-polyfill": "^6.26.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "chokidar": "^1.6.1", + "commander": "^2.11.0", + "convert-source-map": "^1.5.0", + "fs-readdir-recursive": "^1.0.0", + "glob": "^7.1.2", + "lodash": "^4.17.4", + "output-file-sync": "^1.1.2", + "path-is-absolute": "^1.0.1", + "slash": "^1.0.0", + "source-map": "^0.5.6", + "v8flags": "^2.1.1" }, "dependencies": { "babel-runtime": { @@ -531,8 +531,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "glob": { @@ -541,12 +541,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -557,9 +557,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, "babel-core": { @@ -568,25 +568,25 @@ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.0", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.0", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.4", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.0", + "debug": "^2.6.8", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.7", + "slash": "^1.0.0", + "source-map": "^0.5.6" }, "dependencies": { "babel-runtime": { @@ -595,8 +595,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "json5": { @@ -613,10 +613,10 @@ "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0" + "babel-code-frame": "^6.22.0", + "babel-traverse": "^6.23.1", + "babel-types": "^6.23.0", + "babylon": "^6.17.0" } }, "babel-generator": { @@ -625,14 +625,14 @@ "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.6", + "trim-right": "^1.0.1" }, "dependencies": { "babel-runtime": { @@ -641,8 +641,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -653,10 +653,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -665,8 +665,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -677,10 +677,10 @@ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" }, "dependencies": { "babel-runtime": { @@ -689,8 +689,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -701,11 +701,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -714,8 +714,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -726,8 +726,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -736,8 +736,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -748,8 +748,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -758,8 +758,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -770,8 +770,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -780,8 +780,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -792,9 +792,9 @@ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" }, "dependencies": { "babel-runtime": { @@ -803,8 +803,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -815,12 +815,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -829,8 +829,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -841,8 +841,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -851,8 +851,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -863,7 +863,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -872,8 +872,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -890,7 +890,7 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -899,8 +899,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -911,7 +911,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -920,8 +920,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -932,7 +932,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -941,8 +941,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -953,11 +953,11 @@ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" }, "dependencies": { "babel-runtime": { @@ -966,8 +966,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -978,15 +978,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -995,8 +995,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1007,8 +1007,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1017,8 +1017,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1029,7 +1029,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1038,8 +1038,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1050,8 +1050,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1060,8 +1060,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1072,7 +1072,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1081,8 +1081,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1093,9 +1093,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1104,8 +1104,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1116,7 +1116,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1125,8 +1125,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1137,9 +1137,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1148,8 +1148,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1160,10 +1160,10 @@ "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" }, "dependencies": { "babel-runtime": { @@ -1172,8 +1172,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1184,9 +1184,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1195,8 +1195,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1207,9 +1207,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1218,8 +1218,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1230,8 +1230,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1240,8 +1240,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1252,12 +1252,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1266,8 +1266,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1278,8 +1278,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1288,8 +1288,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1300,7 +1300,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1309,8 +1309,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1321,9 +1321,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1332,8 +1332,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1344,7 +1344,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1353,8 +1353,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1365,7 +1365,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1374,8 +1374,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1386,9 +1386,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" }, "dependencies": { "babel-runtime": { @@ -1397,8 +1397,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1409,7 +1409,7 @@ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { - "regenerator-transform": "0.10.1" + "regenerator-transform": "^0.10.0" } }, "babel-plugin-transform-runtime": { @@ -1418,7 +1418,7 @@ "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" }, "dependencies": { "babel-runtime": { @@ -1427,8 +1427,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1439,8 +1439,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" }, "dependencies": { "babel-runtime": { @@ -1449,8 +1449,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1461,9 +1461,9 @@ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "regenerator-runtime": "0.10.5" + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" }, "dependencies": { "babel-runtime": { @@ -1472,8 +1472,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" }, "dependencies": { "regenerator-runtime": { @@ -1498,30 +1498,30 @@ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" } }, "babel-register": { @@ -1530,13 +1530,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "home-or-tmp": "2.0.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" }, "dependencies": { "babel-runtime": { @@ -1545,8 +1545,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1556,7 +1556,7 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", "integrity": "sha1-eIuUtvY04luRvWxd9y1GdFevsAA=", "requires": { - "core-js": "2.5.1" + "core-js": "^2.1.0" } }, "babel-template": { @@ -1565,11 +1565,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.4" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" }, "dependencies": { "babel-runtime": { @@ -1578,8 +1578,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1590,15 +1590,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" }, "dependencies": { "babel-runtime": { @@ -1607,8 +1607,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1619,10 +1619,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" }, "dependencies": { "babel-runtime": { @@ -1631,8 +1631,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -1648,7 +1648,7 @@ "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", "requires": { - "precond": "0.2.3" + "precond": "0.2" } }, "balanced-match": { @@ -1667,7 +1667,7 @@ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "beeper": { @@ -1706,15 +1706,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.15" + "type-is": "~1.6.15" } }, "boom": { @@ -1722,7 +1722,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } }, "boxen": { @@ -1731,13 +1731,13 @@ "integrity": "sha1-Px1AMsMP/qnUsCwyLq8up0HcvOU=", "dev": true, "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.3.0", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "1.0.0" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^1.0.0" }, "dependencies": { "ansi-regex": { @@ -1752,7 +1752,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.0" + "color-convert": "^1.9.0" } }, "camelcase": { @@ -1767,9 +1767,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "has-flag": { @@ -1790,8 +1790,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -1800,7 +1800,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -1809,7 +1809,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -1819,7 +1819,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -1829,9 +1829,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "buffer": { @@ -1839,9 +1839,9 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" }, "dependencies": { "isarray": { @@ -1877,10 +1877,10 @@ "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "requires": { - "dtrace-provider": "0.8.5", - "moment": "2.22.2", - "mv": "2.1.1", - "safe-json-stringify": "1.0.4" + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" } }, "bytes": { @@ -1894,7 +1894,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "0.2.0" + "callsites": "^0.2.0" } }, "callsites": { @@ -1928,8 +1928,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "chai": { @@ -1938,9 +1938,9 @@ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, "requires": { - "assertion-error": "1.0.2", - "deep-eql": "0.1.3", - "type-detect": "1.0.0" + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" } }, "chalk": { @@ -1948,11 +1948,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "check-more-types": { @@ -1967,14 +1967,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" } }, "circular-json": { @@ -1995,12 +1996,12 @@ "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=", "dev": true, "requires": { - "ansi-regex": "2.1.1", - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "memoizee": "0.4.11", - "timers-ext": "0.1.2" + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.12", + "es6-iterator": "2", + "memoizee": "^0.4.3", + "timers-ext": "0.1" } }, "cli-cursor": { @@ -2009,7 +2010,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "^1.0.1" } }, "cli-width": { @@ -2025,8 +2026,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -2082,7 +2083,7 @@ "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "^1.1.1" } }, "color-name": { @@ -2101,7 +2102,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -2130,9 +2131,9 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" }, "dependencies": { "isarray": { @@ -2147,13 +2148,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -2162,7 +2163,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } } } @@ -2182,8 +2183,8 @@ "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", "dev": true, "requires": { - "ini": "1.3.4", - "proto-list": "1.2.4" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, "configstore": { @@ -2192,12 +2193,12 @@ "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", "dev": true, "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.1.11", - "make-dir": "1.1.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.3.0", - "xdg-basedir": "3.0.0" + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" } }, "contains-path": { @@ -2221,8 +2222,8 @@ "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.0.tgz", "integrity": "sha1-4Z/Da1lwkKXU5KOy6j68XilpSiQ=", "requires": { - "async-listener": "0.6.8", - "emitter-listener": "1.0.1" + "async-listener": "^0.6.0", + "emitter-listener": "^1.0.1" } }, "convert-source-map": { @@ -2261,8 +2262,8 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", "requires": { - "object-assign": "4.1.1", - "vary": "1.1.2" + "object-assign": "^4", + "vary": "^1" } }, "create-error-class": { @@ -2271,7 +2272,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "1.0.0" + "capture-stack-trace": "^1.0.0" } }, "cross-spawn": { @@ -2280,9 +2281,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "dependencies": { "lru-cache": { @@ -2291,8 +2292,8 @@ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } } } @@ -2302,7 +2303,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "boom": "5.2.0" + "boom": "5.x.x" }, "dependencies": { "boom": { @@ -2310,7 +2311,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } } } @@ -2342,7 +2343,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.35" + "es5-ext": "^0.10.9" } }, "dashdash": { @@ -2350,7 +2351,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "dateformat": { @@ -2408,7 +2409,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "2.0.0" + "strip-bom": "^2.0.0" }, "dependencies": { "strip-bom": { @@ -2417,7 +2418,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -2428,7 +2429,7 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "1.0.2" + "clone": "^1.0.2" }, "dependencies": { "clone": { @@ -2445,13 +2446,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.4.5" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "delayed-stream": { @@ -2481,7 +2482,7 @@ "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", "dev": true, "requires": { - "fs-exists-sync": "0.1.0" + "fs-exists-sync": "^0.1.0" } }, "detect-indent": { @@ -2490,7 +2491,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "diff": { @@ -2505,8 +2506,8 @@ "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true, "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" + "esutils": "^2.0.2", + "isarray": "^1.0.0" }, "dependencies": { "isarray": { @@ -2523,7 +2524,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "1.0.1" + "is-obj": "^1.0.0" } }, "dottie": { @@ -2537,7 +2538,7 @@ "integrity": "sha1-mOu6Ihr6xG4cOf02hY2Pk2dSS5I=", "optional": true, "requires": { - "nan": "2.7.0" + "nan": "^2.3.3" } }, "duplexer": { @@ -2552,7 +2553,7 @@ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "dev": true, "requires": { - "readable-stream": "1.1.14" + "readable-stream": "~1.1.9" } }, "duplexer3": { @@ -2567,7 +2568,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "ecdsa-sig-formatter": { @@ -2575,7 +2576,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "editorconfig": { @@ -2584,11 +2585,11 @@ "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==", "dev": true, "requires": { - "bluebird": "3.5.1", - "commander": "2.11.0", - "lru-cache": "3.2.0", - "semver": "5.4.1", - "sigmund": "1.0.1" + "bluebird": "^3.0.5", + "commander": "^2.9.0", + "lru-cache": "^3.2.0", + "semver": "^5.1.0", + "sigmund": "^1.0.1" }, "dependencies": { "lru-cache": { @@ -2597,7 +2598,7 @@ "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", "dev": true, "requires": { - "pseudomap": "1.0.2" + "pseudomap": "^1.0.1" } } } @@ -2612,11 +2613,11 @@ "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-11.0.1.tgz", "integrity": "sha1-0YBoTGvefs+g+iTmL6HIcu6uCOc=", "requires": { - "chalk": "1.1.3", - "forever-agent": "0.6.1", - "lodash": "3.10.1", - "lodash-compat": "3.10.2", - "promise": "7.3.1" + "chalk": "^1.0.0", + "forever-agent": "^0.6.0", + "lodash": "^3.10.0", + "lodash-compat": "^3.0.0", + "promise": "^7.1.1" }, "dependencies": { "lodash": { @@ -2652,7 +2653,7 @@ "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "dev": true, "requires": { - "once": "1.3.3" + "once": "~1.3.0" }, "dependencies": { "once": { @@ -2661,7 +2662,7 @@ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } } } @@ -2672,7 +2673,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "es5-ext": { @@ -2681,8 +2682,8 @@ "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=", "dev": true, "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" + "es6-iterator": "~2.0.1", + "es6-symbol": "~3.1.1" } }, "es6-iterator": { @@ -2691,9 +2692,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-symbol": "3.1.1" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, "es6-map": { @@ -2702,12 +2703,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" } }, "es6-promise": { @@ -2722,11 +2723,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" + "event-emitter": "~0.3.5" } }, "es6-symbol": { @@ -2735,8 +2736,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35" + "d": "1", + "es5-ext": "~0.10.14" } }, "es6-weak-map": { @@ -2745,10 +2746,10 @@ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" } }, "escape-html": { @@ -2767,10 +2768,10 @@ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "eslint": { @@ -2779,41 +2780,41 @@ "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "chalk": "1.1.3", - "concat-stream": "1.6.0", - "debug": "2.6.9", - "doctrine": "2.0.0", - "escope": "3.6.0", - "espree": "3.5.1", - "esquery": "1.0.0", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "glob": "7.1.2", - "globals": "9.18.0", - "ignore": "3.3.7", - "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.16.1", - "is-resolvable": "1.0.0", - "js-yaml": "3.10.0", - "json-stable-stringify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", - "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" + "babel-code-frame": "^6.16.0", + "chalk": "^1.1.3", + "concat-stream": "^1.5.2", + "debug": "^2.1.1", + "doctrine": "^2.0.0", + "escope": "^3.6.0", + "espree": "^3.4.0", + "esquery": "^1.0.0", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "glob": "^7.0.3", + "globals": "^9.14.0", + "ignore": "^3.2.0", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "levn": "^0.3.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.7.5", + "strip-bom": "^3.0.0", + "strip-json-comments": "~2.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" }, "dependencies": { "glob": { @@ -2822,12 +2823,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "user-home": { @@ -2836,7 +2837,7 @@ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.0" } } } @@ -2847,7 +2848,7 @@ "integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==", "dev": true, "requires": { - "eslint-restricted-globals": "0.1.1" + "eslint-restricted-globals": "^0.1.1" } }, "eslint-import-resolver-node": { @@ -2856,8 +2857,8 @@ "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", "dev": true, "requires": { - "debug": "2.6.9", - "resolve": "1.5.0" + "debug": "^2.6.8", + "resolve": "^1.2.0" } }, "eslint-module-utils": { @@ -2866,8 +2867,8 @@ "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", "dev": true, "requires": { - "debug": "2.6.9", - "pkg-dir": "1.0.0" + "debug": "^2.6.8", + "pkg-dir": "^1.0.0" } }, "eslint-plugin-import": { @@ -2876,16 +2877,16 @@ "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", "dev": true, "requires": { - "builtin-modules": "1.1.1", - "contains-path": "0.1.0", - "debug": "2.6.9", + "builtin-modules": "^1.1.1", + "contains-path": "^0.1.0", + "debug": "^2.6.8", "doctrine": "1.5.0", - "eslint-import-resolver-node": "0.3.1", - "eslint-module-utils": "2.1.1", - "has": "1.0.1", - "lodash.cond": "4.5.2", - "minimatch": "3.0.4", - "read-pkg-up": "2.0.0" + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.1.1", + "has": "^1.0.1", + "lodash.cond": "^4.3.0", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0" }, "dependencies": { "doctrine": { @@ -2894,8 +2895,8 @@ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" + "esutils": "^2.0.2", + "isarray": "^1.0.0" } }, "isarray": { @@ -2918,8 +2919,8 @@ "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", "dev": true, "requires": { - "acorn": "5.2.1", - "acorn-jsx": "3.0.1" + "acorn": "^5.1.1", + "acorn-jsx": "^3.0.0" } }, "esprima": { @@ -2934,7 +2935,7 @@ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.0.0" } }, "esrecurse": { @@ -2943,8 +2944,8 @@ "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", "dev": true, "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" + "estraverse": "^4.1.0", + "object-assign": "^4.0.1" } }, "estraverse": { @@ -2970,8 +2971,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35" + "d": "1", + "es5-ext": "~0.10.14" } }, "event-stream": { @@ -2980,13 +2981,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "0.1.1", - "from": "0.1.7", - "map-stream": "0.1.0", + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", "pause-stream": "0.0.11", - "split": "0.3.3", - "stream-combiner": "0.0.4", - "through": "2.3.8" + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" } }, "events": { @@ -3000,13 +3001,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "exit-hook": { @@ -3021,7 +3022,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "expand-range": { @@ -3030,7 +3031,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "^2.1.0" } }, "expand-tilde": { @@ -3039,7 +3040,7 @@ "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.1" } }, "express": { @@ -3047,36 +3048,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", "requires": { - "accepts": "1.3.4", + "accepts": "~1.3.4", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.1", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.1", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", + "proxy-addr": "~2.0.2", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.1", "serve-static": "1.13.1", "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", + "statuses": "~1.3.1", + "type-is": "~1.6.15", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "setprototypeof": { @@ -3096,8 +3097,8 @@ "resolved": "https://registry.npmjs.org/express-list-routes/-/express-list-routes-0.1.4.tgz", "integrity": "sha1-xlwxw/thnHnAVD97TsToMFbs5hY=", "requires": { - "colors": "1.1.2", - "lodash": "3.10.1" + "colors": "^1.0.3", + "lodash": "^3.0.0" }, "dependencies": { "lodash": { @@ -3112,7 +3113,7 @@ "resolved": "https://registry.npmjs.org/express-request-id/-/express-request-id-1.4.0.tgz", "integrity": "sha1-J3ssCUmAPmgQTJ1Fw+aJNPlr9aI=", "requires": { - "uuid": "3.1.0" + "uuid": "^3.0.1" } }, "express-sanitizer": { @@ -3128,7 +3129,7 @@ "resolved": "https://registry.npmjs.org/express-validation/-/express-validation-0.6.0.tgz", "integrity": "sha1-DXf0r8flixIBat7FmzJb7v2dwmg=", "requires": { - "lodash": "4.17.4" + "lodash": "^4.9.0" } }, "extend": { @@ -3142,7 +3143,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "extsprintf": { @@ -3156,8 +3157,8 @@ "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", "dev": true, "requires": { - "chalk": "1.1.3", - "time-stamp": "1.1.0" + "chalk": "^1.1.1", + "time-stamp": "^1.0.0" } }, "fast-deep-equal": { @@ -3182,8 +3183,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "file-entry-cache": { @@ -3192,8 +3193,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" } }, "filename-regex": { @@ -3208,8 +3209,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" + "glob": "^7.0.3", + "minimatch": "^3.0.3" }, "dependencies": { "glob": { @@ -3218,12 +3219,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -3234,11 +3235,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, "finalhandler": { @@ -3247,12 +3248,12 @@ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "requires": { "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" }, "dependencies": { "statuses": { @@ -3274,8 +3275,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "findup-sync": { @@ -3284,10 +3285,10 @@ "integrity": "sha1-b35LV7buOkA3tEFOrt6j9Y9x4Ow=", "dev": true, "requires": { - "detect-file": "0.1.0", - "is-glob": "2.0.1", - "micromatch": "2.3.11", - "resolve-dir": "0.1.1" + "detect-file": "^0.1.0", + "is-glob": "^2.0.1", + "micromatch": "^2.3.7", + "resolve-dir": "^0.1.0" } }, "fined": { @@ -3296,11 +3297,11 @@ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0", - "object.pick": "1.3.0", - "parse-filepath": "1.0.1" + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" }, "dependencies": { "expand-tilde": { @@ -3309,7 +3310,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1" + "homedir-polyfill": "^1.0.1" } } } @@ -3332,10 +3333,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" } }, "follow-redirects": { @@ -3343,7 +3344,7 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.6.tgz", "integrity": "sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA==", "requires": { - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -3368,7 +3369,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "forever-agent": { @@ -3381,9 +3382,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "formatio": { @@ -3392,7 +3393,7 @@ "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", "dev": true, "requires": { - "samsam": "1.1.2" + "samsam": "~1.1" } }, "formidable": { @@ -3428,9 +3429,9 @@ "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "fs-readdir-recursive": { @@ -3445,6 +3446,542 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3457,7 +3994,7 @@ "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "dev": true, "requires": { - "globule": "0.1.0" + "globule": "~0.1.0" } }, "generate-function": { @@ -3472,7 +4009,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "1.0.2" + "is-property": "^1.0.0" } }, "generic-pool": { @@ -3497,7 +4034,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { @@ -3505,11 +4042,11 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-base": { @@ -3518,8 +4055,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" } }, "glob-parent": { @@ -3528,7 +4065,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "glob-stream": { @@ -3537,12 +4074,12 @@ "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "dev": true, "requires": { - "glob": "4.5.3", - "glob2base": "0.0.12", - "minimatch": "2.0.10", - "ordered-read-streams": "0.1.0", - "through2": "0.6.5", - "unique-stream": "1.0.0" + "glob": "^4.3.1", + "glob2base": "^0.0.12", + "minimatch": "^2.0.1", + "ordered-read-streams": "^0.1.0", + "through2": "^0.6.1", + "unique-stream": "^1.0.0" }, "dependencies": { "glob": { @@ -3551,10 +4088,10 @@ "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", "dev": true, "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "2.0.10", - "once": "1.4.0" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" } }, "minimatch": { @@ -3563,7 +4100,7 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "^1.0.0" } }, "readable-stream": { @@ -3572,10 +4109,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "through2": { @@ -3584,8 +4121,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" } } } @@ -3596,7 +4133,7 @@ "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "dev": true, "requires": { - "gaze": "0.5.2" + "gaze": "^0.5.1" } }, "glob2base": { @@ -3605,7 +4142,7 @@ "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", "dev": true, "requires": { - "find-index": "0.1.1" + "find-index": "^0.1.1" } }, "global-dirs": { @@ -3614,7 +4151,7 @@ "integrity": "sha1-ENNAOeDfBCcuJizyQiT3IJQ0308=", "dev": true, "requires": { - "ini": "1.3.4" + "ini": "^1.3.4" } }, "global-modules": { @@ -3623,8 +4160,8 @@ "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", "dev": true, "requires": { - "global-prefix": "0.1.5", - "is-windows": "0.2.0" + "global-prefix": "^0.1.4", + "is-windows": "^0.2.0" } }, "global-prefix": { @@ -3633,10 +4170,10 @@ "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1", - "ini": "1.3.4", - "is-windows": "0.2.0", - "which": "1.3.0" + "homedir-polyfill": "^1.0.0", + "ini": "^1.3.4", + "is-windows": "^0.2.0", + "which": "^1.2.12" } }, "globals": { @@ -3651,12 +4188,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "dependencies": { "glob": { @@ -3665,12 +4202,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -3681,9 +4218,9 @@ "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "dev": true, "requires": { - "glob": "3.1.21", - "lodash": "1.0.2", - "minimatch": "0.2.14" + "glob": "~3.1.21", + "lodash": "~1.0.1", + "minimatch": "~0.2.11" }, "dependencies": { "glob": { @@ -3692,9 +4229,9 @@ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "dev": true, "requires": { - "graceful-fs": "1.2.3", - "inherits": "1.0.2", - "minimatch": "0.2.14" + "graceful-fs": "~1.2.0", + "inherits": "1", + "minimatch": "~0.2.11" } }, "graceful-fs": { @@ -3721,8 +4258,8 @@ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "dev": true, "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" + "lru-cache": "2", + "sigmund": "~1.0.0" } } } @@ -3733,7 +4270,7 @@ "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", "dev": true, "requires": { - "sparkles": "1.0.0" + "sparkles": "^1.0.0" } }, "got": { @@ -3742,17 +4279,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.0", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" } }, "graceful-fs": { @@ -3773,19 +4310,19 @@ "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { - "archy": "1.0.0", - "chalk": "1.1.3", - "deprecated": "0.0.1", - "gulp-util": "3.0.8", - "interpret": "1.0.4", - "liftoff": "2.3.0", - "minimist": "1.2.0", - "orchestrator": "0.3.8", - "pretty-hrtime": "1.0.3", - "semver": "4.3.6", - "tildify": "1.2.0", - "v8flags": "2.1.1", - "vinyl-fs": "0.3.14" + "archy": "^1.0.0", + "chalk": "^1.0.0", + "deprecated": "^0.0.1", + "gulp-util": "^3.0.0", + "interpret": "^1.0.0", + "liftoff": "^2.1.0", + "minimist": "^1.1.0", + "orchestrator": "^0.3.0", + "pretty-hrtime": "^1.0.0", + "semver": "^4.1.0", + "tildify": "^1.0.0", + "v8flags": "^2.0.2", + "vinyl-fs": "^0.3.0" }, "dependencies": { "minimist": { @@ -3808,8 +4345,8 @@ "integrity": "sha1-Jh2xhuGDl/7z9qLCLpwxW/qIrgw=", "dev": true, "requires": { - "chalk": "1.1.3", - "object-assign": "3.0.0" + "chalk": "^1.0.0", + "object-assign": "^3.0.0" }, "dependencies": { "object-assign": { @@ -3826,24 +4363,24 @@ "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "dev": true, "requires": { - "array-differ": "1.0.0", - "array-uniq": "1.0.3", - "beeper": "1.1.1", - "chalk": "1.1.3", - "dateformat": "2.2.0", - "fancy-log": "1.3.0", - "gulplog": "1.0.0", - "has-gulplog": "0.1.0", - "lodash._reescape": "3.0.0", - "lodash._reevaluate": "3.0.0", - "lodash._reinterpolate": "3.0.0", - "lodash.template": "3.6.2", - "minimist": "1.2.0", - "multipipe": "0.1.2", - "object-assign": "3.0.0", + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", "replace-ext": "0.0.1", - "through2": "2.0.3", - "vinyl": "0.5.3" + "through2": "^2.0.0", + "vinyl": "^0.5.0" }, "dependencies": { "minimist": { @@ -3866,7 +4403,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "1.0.0" + "glogg": "^1.0.0" } }, "handlebars": { @@ -3875,10 +4412,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "dependencies": { "source-map": { @@ -3887,7 +4424,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -3902,8 +4439,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "5.5.1", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" }, "dependencies": { "ajv": { @@ -3911,10 +4448,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } } } @@ -3925,7 +4462,7 @@ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.0.2" } }, "has-ansi": { @@ -3933,7 +4470,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-flag": { @@ -3948,7 +4485,7 @@ "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "sparkles": "1.0.0" + "sparkles": "^1.0.0" } }, "hawk": { @@ -3956,10 +4493,10 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" } }, "hoek": { @@ -3973,8 +4510,8 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" } }, "homedir-polyfill": { @@ -3983,7 +4520,7 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "1.0.0" + "parse-passwd": "^1.0.0" } }, "hosted-git-info": { @@ -3997,7 +4534,7 @@ "resolved": "https://registry.npmjs.org/http-aws-es/-/http-aws-es-1.1.3.tgz", "integrity": "sha1-ZJUYQ7XFETBQclcNfCxQn3gUTWs=", "requires": { - "aws-sdk": "2.143.0" + "aws-sdk": "^2.2.19" } }, "http-errors": { @@ -4008,7 +4545,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "statuses": ">= 1.3.1 < 2" } }, "http-signature": { @@ -4016,9 +4553,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "iconv-lite": { @@ -4031,11 +4568,11 @@ "resolved": "https://registry.npmjs.org/idtoken-verifier/-/idtoken-verifier-1.2.0.tgz", "integrity": "sha512-8jmmFHwdPz8L73zGNAXHHOV9yXNC+Z0TUBN5rafpoaFaLFltlIFr1JkQa3FYAETP23eSsulVw0sBiwrE8jqbUg==", "requires": { - "base64-js": "1.2.1", - "crypto-js": "3.1.9-1", - "jsbn": "0.1.1", - "superagent": "3.8.3", - "url-join": "1.1.0" + "base64-js": "^1.2.0", + "crypto-js": "^3.1.9-1", + "jsbn": "^0.1.0", + "superagent": "^3.8.2", + "url-join": "^1.1.0" }, "dependencies": { "debug": { @@ -4066,13 +4603,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -4080,7 +4617,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "superagent": { @@ -4088,16 +4625,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "3.1.0", - "extend": "3.0.1", - "form-data": "2.3.1", - "formidable": "1.2.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.6" + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" } } } @@ -4141,8 +4678,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -4162,19 +4699,19 @@ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.4", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" } }, "interpret": { @@ -4189,7 +4726,7 @@ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "invert-kv": { @@ -4209,8 +4746,8 @@ "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", "dev": true, "requires": { - "is-relative": "0.2.1", - "is-windows": "0.2.0" + "is-relative": "^0.2.1", + "is-windows": "^0.2.0" } }, "is-arrayish": { @@ -4225,7 +4762,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.10.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -4239,7 +4776,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-dotfile": { @@ -4254,7 +4791,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -4275,7 +4812,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -4284,7 +4821,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-glob": { @@ -4293,7 +4830,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "is-installed-globally": { @@ -4302,8 +4839,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "0.1.0", - "is-path-inside": "1.0.0" + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" } }, "is-my-json-valid": { @@ -4312,10 +4849,10 @@ "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", "dev": true, "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" } }, "is-npm": { @@ -4330,7 +4867,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-obj": { @@ -4351,7 +4888,7 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "1.0.0" + "is-path-inside": "^1.0.0" } }, "is-path-inside": { @@ -4360,7 +4897,7 @@ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-object": { @@ -4369,7 +4906,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -4416,7 +4953,7 @@ "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", "dev": true, "requires": { - "is-unc-path": "0.1.2" + "is-unc-path": "^0.1.1" } }, "is-resolvable": { @@ -4425,7 +4962,7 @@ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", "dev": true, "requires": { - "tryit": "1.0.3" + "tryit": "^1.0.1" } }, "is-retry-allowed": { @@ -4451,7 +4988,7 @@ "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", "dev": true, "requires": { - "unc-path-regex": "0.1.2" + "unc-path-regex": "^0.1.0" } }, "is-utf8": { @@ -4510,14 +5047,14 @@ "integrity": "sha1-BglrwI6Yuq10Sq5Gli2N+frGPQg=", "dev": true, "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "istanbul-api": "1.2.1", - "js-yaml": "3.10.0", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "which": "1.3.0", - "wordwrap": "1.0.0" + "abbrev": "1.0.x", + "async": "1.x", + "istanbul-api": "^1.0.0-alpha", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "which": "^1.1.1", + "wordwrap": "^1.0.0" } }, "istanbul-api": { @@ -4526,17 +5063,17 @@ "integrity": "sha512-oFCwXvd65amgaPCzqrR+a2XjanS1MvpXN6l/MlMUTv6uiA1NOgGX+I0uyq8Lg3GDxsxPsaP1049krz3hIJ5+KA==", "dev": true, "requires": { - "async": "2.5.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.1.1", - "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.9.1", - "istanbul-lib-report": "1.1.2", - "istanbul-lib-source-maps": "1.2.2", - "istanbul-reports": "1.1.3", - "js-yaml": "3.10.0", - "mkdirp": "0.5.1", - "once": "1.4.0" + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.1.1", + "istanbul-lib-hook": "^1.1.0", + "istanbul-lib-instrument": "^1.9.1", + "istanbul-lib-report": "^1.1.2", + "istanbul-lib-source-maps": "^1.2.2", + "istanbul-reports": "^1.1.3", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" }, "dependencies": { "async": { @@ -4545,7 +5082,7 @@ "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.14.0" } } } @@ -4562,7 +5099,7 @@ "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { - "append-transform": "0.4.0" + "append-transform": "^0.4.0" } }, "istanbul-lib-instrument": { @@ -4571,13 +5108,13 @@ "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", "dev": true, "requires": { - "babel-generator": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.1.1", + "semver": "^5.3.0" } }, "istanbul-lib-report": { @@ -4586,10 +5123,10 @@ "integrity": "sha512-UTv4VGx+HZivJQwAo1wnRwe1KTvFpfi/NYwN7DcsrdzMXwpRT/Yb6r4SBPoHWj4VuQPakR32g4PUUeyKkdDkBA==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.1.1", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.1.1", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" }, "dependencies": { "supports-color": { @@ -4598,7 +5135,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -4609,11 +5146,11 @@ "integrity": "sha512-8BfdqSfEdtip7/wo1RnrvLpHVEd8zMZEDmOFEnpC6dg0vXflHt9nvoAyQUzig2uMSXfF2OBEYBV3CVjIL9JvaQ==", "dev": true, "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.1.1", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.1.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" }, "dependencies": { "debug": { @@ -4631,12 +5168,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "rimraf": { @@ -4645,7 +5182,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } } } @@ -4656,7 +5193,7 @@ "integrity": "sha512-ZEelkHh8hrZNI5xDaKwPMFwDsUf5wIEI2bXAFGp1e6deR2mnEKBPhLJEgr4ZBt8Gi6Mj38E/C8kcy9XLggVO2Q==", "dev": true, "requires": { - "handlebars": "4.0.11" + "handlebars": "^4.0.3" } }, "jade": { @@ -4693,10 +5230,10 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-8.4.2.tgz", "integrity": "sha1-vXd0ZY/pkFjYmU7R1LmWJITruFk=", "requires": { - "hoek": "4.2.0", - "isemail": "2.2.1", - "moment": "2.22.2", - "topo": "2.0.2" + "hoek": "4.x.x", + "isemail": "2.x.x", + "moment": "2.x.x", + "topo": "2.x.x" } }, "join-component": { @@ -4710,10 +5247,10 @@ "integrity": "sha512-6YX1g+lIl0/JDxjFFbgj7fz6i0bWFa2Hdc7PfGqFhynaEiYe1NJ3R1nda0VGaRiGU82OllR+EGDoWFpGr3k5Kg==", "dev": true, "requires": { - "config-chain": "1.1.11", - "editorconfig": "0.13.3", - "mkdirp": "0.5.1", - "nopt": "3.0.6" + "config-chain": "~1.1.5", + "editorconfig": "^0.13.2", + "mkdirp": "~0.5.0", + "nopt": "~3.0.1" } }, "js-cookie": { @@ -4738,8 +5275,8 @@ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { - "argparse": "1.0.9", - "esprima": "4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -4769,7 +5306,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -4788,7 +5325,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "jsonify": { @@ -4808,15 +5345,15 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", "integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==", "requires": { - "jws": "3.1.5", - "lodash.includes": "4.3.0", - "lodash.isboolean": "3.0.3", - "lodash.isinteger": "4.0.4", - "lodash.isnumber": "3.0.3", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.once": "4.1.1", - "ms": "2.1.1" + "jws": "^3.1.5", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1" }, "dependencies": { "ms": { @@ -4844,7 +5381,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "jwks-rsa": { @@ -4853,11 +5390,11 @@ "integrity": "sha512-xg+fw7FOV4eGdDIEMqQJvPLmFv85h4uN+j/GKwJZAxlCrDQpM8ov1F709xKGEp/dG3l4TUxoSOeN6YK7+KpinQ==", "requires": { "@types/express-jwt": "0.0.34", - "debug": "2.6.9", - "limiter": "1.1.2", - "lru-memoizer": "1.11.1", - "ms": "2.0.0", - "request": "2.83.0" + "debug": "^2.2.0", + "limiter": "^1.1.0", + "lru-memoizer": "^1.6.0", + "ms": "^2.0.0", + "request": "^2.73.0" } }, "jws": { @@ -4865,8 +5402,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", "requires": { - "jwa": "1.1.6", - "safe-buffer": "5.1.1" + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" } }, "kind-of": { @@ -4875,7 +5412,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "latest-version": { @@ -4884,7 +5421,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "4.0.1" + "package-json": "^4.0.0" } }, "lazy-ass": { @@ -4906,7 +5443,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "le_node": { @@ -4940,8 +5477,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "libpq": { @@ -4950,7 +5487,7 @@ "integrity": "sha1-wt6xIeKPf4S9OyRRr/9otmY+dPk=", "requires": { "bindings": "1.2.1", - "nan": "2.7.0" + "nan": "^2.3.0" } }, "liftoff": { @@ -4959,15 +5496,15 @@ "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", "dev": true, "requires": { - "extend": "3.0.1", - "findup-sync": "0.4.3", - "fined": "1.1.0", - "flagged-respawn": "0.3.2", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.mapvalues": "4.6.0", - "rechoir": "0.6.2", - "resolve": "1.5.0" + "extend": "^3.0.0", + "findup-sync": "^0.4.2", + "fined": "^1.0.1", + "flagged-respawn": "^0.3.2", + "lodash.isplainobject": "^4.0.4", + "lodash.isstring": "^4.0.1", + "lodash.mapvalues": "^4.4.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" }, "dependencies": { "findup-sync": { @@ -4976,10 +5513,10 @@ "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", "dev": true, "requires": { - "detect-file": "0.1.0", - "is-glob": "2.0.1", - "micromatch": "2.3.11", - "resolve-dir": "0.1.1" + "detect-file": "^0.1.0", + "is-glob": "^2.0.1", + "micromatch": "^2.3.7", + "resolve-dir": "^0.1.0" } } } @@ -4995,10 +5532,10 @@ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" } }, "locate-path": { @@ -5007,8 +5544,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, "dependencies": { "path-exists": { @@ -5040,8 +5577,8 @@ "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", "dev": true, "requires": { - "lodash._basecopy": "3.0.1", - "lodash.keys": "3.1.2" + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" } }, "lodash._basecopy": { @@ -5074,9 +5611,9 @@ "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", "dev": true, "requires": { - "lodash._bindcallback": "3.0.1", - "lodash._isiterateecall": "3.0.9", - "lodash.restparam": "3.6.1" + "lodash._bindcallback": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash.restparam": "^3.0.0" } }, "lodash._getnative": { @@ -5121,9 +5658,9 @@ "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", "dev": true, "requires": { - "lodash._baseassign": "3.2.0", - "lodash._createassigner": "3.1.1", - "lodash.keys": "3.1.2" + "lodash._baseassign": "^3.0.0", + "lodash._createassigner": "^3.0.0", + "lodash.keys": "^3.0.0" } }, "lodash.cond": { @@ -5138,8 +5675,8 @@ "integrity": "sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw=", "dev": true, "requires": { - "lodash.assign": "3.2.0", - "lodash.restparam": "3.6.1" + "lodash.assign": "^3.0.0", + "lodash.restparam": "^3.0.0" } }, "lodash.escape": { @@ -5148,7 +5685,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "3.0.1" + "lodash._root": "^3.0.0" } }, "lodash.includes": { @@ -5199,9 +5736,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" } }, "lodash.mapvalues": { @@ -5227,15 +5764,15 @@ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "lodash._basecopy": "3.0.1", - "lodash._basetostring": "3.0.1", - "lodash._basevalues": "3.0.0", - "lodash._isiterateecall": "3.0.9", - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0", - "lodash.keys": "3.1.2", - "lodash.restparam": "3.6.1", - "lodash.templatesettings": "3.1.1" + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" } }, "lodash.templatesettings": { @@ -5244,8 +5781,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0" + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" } }, "lolex": { @@ -5266,7 +5803,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" } }, "lowercase-keys": { @@ -5286,10 +5823,10 @@ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-1.11.1.tgz", "integrity": "sha1-BpP2EAWTkUwC4ZK/m42TiEy/UNM=", "requires": { - "lock": "0.1.4", - "lodash": "4.5.1", - "lru-cache": "4.0.2", - "very-fast-args": "1.1.0" + "lock": "~0.1.2", + "lodash": "~4.5.1", + "lru-cache": "~4.0.0", + "very-fast-args": "^1.1.0" }, "dependencies": { "lodash": { @@ -5302,8 +5839,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" } } } @@ -5314,7 +5851,7 @@ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "requires": { - "es5-ext": "0.10.35" + "es5-ext": "~0.10.2" } }, "make-dir": { @@ -5323,7 +5860,7 @@ "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -5357,7 +5894,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.1.0" + "mimic-fn": "^1.0.0" } }, "memoizee": { @@ -5366,14 +5903,23 @@ "integrity": "sha1-vemBdmPJ5A/bKk6hw2cpYIeujI8=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-weak-map": "2.0.2", - "event-emitter": "0.3.5", - "is-promise": "2.1.0", - "lru-queue": "0.1.0", - "next-tick": "1.0.0", - "timers-ext": "0.1.2" + "d": "1", + "es5-ext": "^0.10.30", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.2" + } + }, + "memwatch-next": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memwatch-next/-/memwatch-next-0.3.0.tgz", + "integrity": "sha1-IREFD5qQbgqi1ypOwPAInHhyb48=", + "requires": { + "bindings": "^1.2.1", + "nan": "^2.3.2" } }, "merge-descriptors": { @@ -5387,9 +5933,9 @@ "integrity": "sha1-49r41d7hDdLc59SuiNYrvud0drQ=", "requires": { "debug": "2.6.9", - "methods": "1.1.2", - "parseurl": "1.3.2", - "vary": "1.1.2" + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" } }, "methods": { @@ -5403,19 +5949,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "millisecond": { @@ -5438,7 +5984,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "1.30.0" + "mime-db": "~1.30.0" } }, "mimic-fn": { @@ -5452,7 +5998,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -5513,8 +6059,8 @@ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", "dev": true, "requires": { - "inherits": "2.0.3", - "minimatch": "0.3.0" + "inherits": "2", + "minimatch": "0.3" } }, "minimatch": { @@ -5523,8 +6069,8 @@ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", "dev": true, "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" + "lru-cache": "2", + "sigmund": "~1.0.0" } }, "ms": { @@ -5551,7 +6097,7 @@ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz", "integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=", "requires": { - "moment": "2.22.2" + "moment": ">= 2.9.0" } }, "ms": { @@ -5580,9 +6126,9 @@ "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", "optional": true, "requires": { - "mkdirp": "0.5.1", - "ncp": "2.0.0", - "rimraf": "2.4.5" + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" } }, "nan": { @@ -5625,16 +6171,16 @@ "integrity": "sha1-mWpW3EnZ8Wu/G3ik3gjxNjSzh40=", "dev": true, "requires": { - "chokidar": "1.7.0", - "debug": "2.6.9", - "es6-promise": "3.3.1", - "ignore-by-default": "1.0.1", - "lodash.defaults": "3.1.2", - "minimatch": "3.0.4", - "ps-tree": "1.1.0", - "touch": "3.1.0", + "chokidar": "^1.7.0", + "debug": "^2.6.8", + "es6-promise": "^3.3.1", + "ignore-by-default": "^1.0.1", + "lodash.defaults": "^3.1.2", + "minimatch": "^3.0.4", + "ps-tree": "^1.1.0", + "touch": "^3.1.0", "undefsafe": "0.0.3", - "update-notifier": "2.3.0" + "update-notifier": "^2.2.0" } }, "nopt": { @@ -5643,7 +6189,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1.0.9" + "abbrev": "1" } }, "normalize-package-data": { @@ -5652,10 +6198,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { @@ -5664,7 +6210,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } }, "npm-run-path": { @@ -5673,7 +6219,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "number-is-nan": { @@ -5698,10 +6244,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "1.0.1", - "array-slice": "1.0.0", - "for-own": "1.0.0", - "isobject": "3.0.1" + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" }, "dependencies": { "for-own": { @@ -5710,7 +6256,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "isobject": { @@ -5727,8 +6273,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" } }, "object.pick": { @@ -5737,7 +6283,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -5761,7 +6307,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -5776,8 +6322,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" }, "dependencies": { "wordwrap": { @@ -5794,12 +6340,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" } }, "orchestrator": { @@ -5808,9 +6354,9 @@ "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "dev": true, "requires": { - "end-of-stream": "0.1.5", - "sequencify": "0.0.7", - "stream-consume": "0.1.0" + "end-of-stream": "~0.1.5", + "sequencify": "~0.0.7", + "stream-consume": "~0.1.0" } }, "ordered-read-streams": { @@ -5830,9 +6376,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "os-tmpdir": { @@ -5847,9 +6393,9 @@ "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1", - "object-assign": "4.1.1" + "graceful-fs": "^4.1.4", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.0" } }, "p-finally": { @@ -5870,7 +6416,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.1.0" + "p-limit": "^1.1.0" } }, "package-json": { @@ -5879,10 +6425,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "6.7.1", - "registry-auth-token": "3.3.1", - "registry-url": "3.1.0", - "semver": "5.4.1" + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" } }, "packet-reader": { @@ -5896,9 +6442,9 @@ "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", "dev": true, "requires": { - "is-absolute": "0.2.6", - "map-cache": "0.2.2", - "path-root": "0.1.1" + "is-absolute": "^0.2.3", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" } }, "parse-glob": { @@ -5907,10 +6453,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" } }, "parse-json": { @@ -5919,7 +6465,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.1" + "error-ex": "^1.2.0" } }, "parse-passwd": { @@ -5939,7 +6485,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } }, "path-is-absolute": { @@ -5971,7 +6517,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "0.1.2" + "path-root-regex": "^0.1.0" } }, "path-root-regex": { @@ -5991,7 +6537,7 @@ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "2.3.0" + "pify": "^2.0.0" } }, "pause-stream": { @@ -6000,7 +6546,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "2.3.8" + "through": "~2.3" } }, "performance-now": { @@ -6018,9 +6564,9 @@ "js-string-escape": "1.0.1", "packet-reader": "0.2.0", "pg-connection-string": "0.1.3", - "pg-types": "1.12.1", + "pg-types": "1.*", "pgpass": "0.0.3", - "semver": "4.3.6" + "semver": "^4.1.0" }, "dependencies": { "semver": { @@ -6040,7 +6586,7 @@ "resolved": "https://registry.npmjs.org/pg-native/-/pg-native-1.10.1.tgz", "integrity": "sha1-lOYcy7hafzQ2suUmMVx1gRB/5Aw=", "requires": { - "libpq": "1.8.7", + "libpq": "^1.7.0", "pg-types": "1.6.0", "readable-stream": "1.0.31" }, @@ -6055,10 +6601,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz", "integrity": "sha1-jyUC4LyeOw2huUUgqrtOJgPsr64=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } } } @@ -6068,10 +6614,10 @@ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", "requires": { - "postgres-array": "1.0.2", - "postgres-bytea": "1.0.0", - "postgres-date": "1.0.3", - "postgres-interval": "1.1.1" + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" } }, "pgpass": { @@ -6079,7 +6625,7 @@ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-0.0.3.tgz", "integrity": "sha1-EuZ+NDsxicLzEgbrycwL7//PkUA=", "requires": { - "split": "0.3.3" + "split": "~0.3" } }, "pify": { @@ -6100,7 +6646,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "pkg-dir": { @@ -6109,7 +6655,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "1.1.2" + "find-up": "^1.0.0" } }, "pluralize": { @@ -6138,7 +6684,7 @@ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.1.tgz", "integrity": "sha512-OkuCi9t/3CZmeQreutGgx/OVNv9MKHGIT5jH8KldQ4NLYXkvmT9nDVxEuCENlNwhlGPE374oA/xMqn05G49pHA==", "requires": { - "xtend": "4.0.1" + "xtend": "^4.0.0" } }, "precond": { @@ -6192,7 +6738,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "proto-list": { @@ -6206,7 +6752,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.5.2" } }, @@ -6216,7 +6762,7 @@ "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "dev": true, "requires": { - "event-stream": "3.3.4" + "event-stream": "~3.3.0" } }, "pseudomap": { @@ -6245,8 +6791,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -6255,7 +6801,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -6264,7 +6810,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6275,7 +6821,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6302,10 +6848,10 @@ "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", "dev": true, "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -6322,9 +6868,9 @@ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" } }, "read-pkg-up": { @@ -6333,8 +6879,8 @@ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" }, "dependencies": { "find-up": { @@ -6343,7 +6889,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } } } @@ -6353,10 +6899,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "readdirp": { @@ -6365,10 +6911,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" }, "dependencies": { "isarray": { @@ -6383,13 +6929,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -6398,7 +6944,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } } } @@ -6409,8 +6955,8 @@ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", "mute-stream": "0.0.5" } }, @@ -6430,7 +6976,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "1.5.0" + "resolve": "^1.1.6" } }, "reconnect-core": { @@ -6438,7 +6984,7 @@ "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", "requires": { - "backoff": "2.5.0" + "backoff": "~2.5.0" } }, "redefine": { @@ -6465,9 +7011,9 @@ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.8" + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" }, "dependencies": { "babel-runtime": { @@ -6476,8 +7022,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } } } @@ -6488,7 +7034,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3" + "is-equal-shallow": "^0.1.3" } }, "regexpu-core": { @@ -6497,9 +7043,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" } }, "registry-auth-token": { @@ -6508,8 +7054,8 @@ "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", "dev": true, "requires": { - "rc": "1.2.2", - "safe-buffer": "5.1.1" + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, "registry-url": { @@ -6518,7 +7064,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "1.2.2" + "rc": "^1.0.1" } }, "regjsgen": { @@ -6533,7 +7079,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "0.5.0" + "jsesc": "~0.5.0" }, "dependencies": { "jsesc": { @@ -6573,7 +7119,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "replace-ext": { @@ -6587,28 +7133,28 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" } }, "require-directory": { @@ -6629,8 +7175,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" } }, "resolve": { @@ -6639,7 +7185,7 @@ "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "dev": true, "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "resolve-dir": { @@ -6648,8 +7194,8 @@ "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", "dev": true, "requires": { - "expand-tilde": "1.2.2", - "global-modules": "0.2.3" + "expand-tilde": "^1.2.2", + "global-modules": "^0.2.3" } }, "resolve-from": { @@ -6664,8 +7210,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" } }, "retry-as-promised": { @@ -6673,8 +7219,8 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", "requires": { - "bluebird": "3.5.1", - "debug": "2.6.9" + "bluebird": "^3.4.6", + "debug": "^2.6.9" } }, "right-align": { @@ -6684,7 +7230,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "rimraf": { @@ -6692,7 +7238,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "requires": { - "glob": "6.0.4" + "glob": "^6.0.1" } }, "run-async": { @@ -6701,7 +7247,7 @@ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.3.0" } }, "rx-lite": { @@ -6748,7 +7294,7 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "5.4.1" + "semver": "^5.0.3" } }, "send": { @@ -6757,18 +7303,18 @@ "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", "requires": { "debug": "2.6.9", - "depd": "1.1.1", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.1", + "destroy": "~1.0.4", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.2", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.3.1" }, "dependencies": { "statuses": { @@ -6783,21 +7329,21 @@ "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", "integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", "requires": { - "bluebird": "3.5.1", - "depd": "1.1.1", - "dottie": "1.1.1", + "bluebird": "^3.3.4", + "depd": "^1.1.0", + "dottie": "^1.0.0", "generic-pool": "2.4.2", - "inflection": "1.12.0", + "inflection": "^1.6.0", "lodash": "4.12.0", - "moment": "2.22.2", - "moment-timezone": "0.5.14", - "retry-as-promised": "2.3.2", - "semver": "5.4.1", + "moment": "^2.13.0", + "moment-timezone": "^0.5.4", + "retry-as-promised": "^2.0.0", + "semver": "^5.0.1", "shimmer": "1.1.0", - "terraformer-wkt-parser": "1.1.2", - "toposort-class": "1.0.1", - "uuid": "3.1.0", - "validator": "5.7.0", + "terraformer-wkt-parser": "^1.1.0", + "toposort-class": "^1.0.1", + "uuid": "^3.0.0", + "validator": "^5.2.0", "wkx": "0.2.0" }, "dependencies": { @@ -6814,18 +7360,18 @@ "integrity": "sha1-QwTM5g5JkWlgP4ON7bq0IcmEnnQ=", "dev": true, "requires": { - "bluebird": "3.5.1", - "cli-color": "1.2.0", - "findup-sync": "1.0.0", - "fs-extra": "4.0.2", - "gulp": "3.9.1", - "gulp-help": "1.6.1", - "js-beautify": "1.7.4", - "lodash": "4.17.4", - "moment": "2.22.2", - "resolve": "1.5.0", - "umzug": "1.12.0", - "yargs": "8.0.2" + "bluebird": "^3.5.0", + "cli-color": "~1.2.0", + "findup-sync": "^1.0.0", + "fs-extra": "^4.0.1", + "gulp": "^3.9.1", + "gulp-help": "~1.6.1", + "js-beautify": "^1.6.11", + "lodash": "^4.17.4", + "moment": "^2.17.1", + "resolve": "^1.3.3", + "umzug": "^1.12.0", + "yargs": "^8.0.1" }, "dependencies": { "ansi-regex": { @@ -6846,9 +7392,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" }, "dependencies": { "string-width": { @@ -6857,9 +7403,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -6870,8 +7416,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -6886,7 +7432,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -6897,19 +7443,19 @@ "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", "dev": true, "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" } } } @@ -6925,9 +7471,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.1" } }, @@ -6954,7 +7500,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -6969,9 +7515,9 @@ "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true, "requires": { - "glob": "7.1.2", - "interpret": "1.0.4", - "rechoir": "0.6.2" + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" }, "dependencies": { "glob": { @@ -6980,12 +7526,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -7016,7 +7562,7 @@ "formatio": "1.1.1", "lolex": "1.3.2", "samsam": "1.1.2", - "util": "0.10.3" + "util": ">=0.10.3 <1" } }, "sinon-chai": { @@ -7037,7 +7583,7 @@ "integrity": "sha1-h4+h1E0I7rDyb7IBjvhinrGjq5Q=", "dev": true, "requires": { - "nan": "2.7.0" + "nan": ">=2.5.1" } }, "slice-ansi": { @@ -7051,7 +7597,7 @@ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } }, "source-map": { @@ -7066,7 +7612,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "^0.5.6" } }, "sparkles": { @@ -7081,7 +7627,7 @@ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "dev": true, "requires": { - "spdx-license-ids": "1.2.2" + "spdx-license-ids": "^1.0.2" } }, "spdx-expression-parse": { @@ -7101,7 +7647,7 @@ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "requires": { - "through": "2.3.8" + "through": "2" } }, "sprintf-js": { @@ -7115,14 +7661,14 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" } }, "statuses": { @@ -7136,7 +7682,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "0.1.1" + "duplexer": "~0.1.1" } }, "stream-consume": { @@ -7144,22 +7690,22 @@ "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=" }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -7170,7 +7716,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -7196,16 +7742,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.0.tgz", "integrity": "sha512-71XGWgtn70TNwgmgYa69dPOYg55aU9FCahjUNY03rOrKvaTCaU3b9MeZmqonmf9Od96SCxr3vGfEAnhM7dtxCw==", "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "3.1.0", - "extend": "3.0.1", - "form-data": "2.3.1", - "formidable": "1.1.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.3" + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" }, "dependencies": { "debug": { @@ -7226,13 +7772,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -7240,7 +7786,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } } } @@ -7256,8 +7802,8 @@ "integrity": "sha1-oFgIHXiPFRXUcA11Aogea3WeRM0=", "dev": true, "requires": { - "methods": "1.1.2", - "superagent": "2.3.0" + "methods": "1.x", + "superagent": "^2.0.0" }, "dependencies": { "form-data": { @@ -7266,9 +7812,9 @@ "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", "dev": true, "requires": { - "async": "1.5.2", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "async": "^1.5.2", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.10" } }, "isarray": { @@ -7283,13 +7829,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -7298,7 +7844,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "superagent": { @@ -7307,16 +7853,16 @@ "integrity": "sha1-cDUpoHFOV+EjlZ3e+84ZOy5Q0RU=", "dev": true, "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "2.6.9", - "extend": "3.0.1", + "component-emitter": "^1.2.0", + "cookiejar": "^2.0.6", + "debug": "^2.2.0", + "extend": "^3.0.0", "form-data": "1.0.0-rc4", - "formidable": "1.1.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.3" + "formidable": "^1.0.17", + "methods": "^1.1.1", + "mime": "^1.3.4", + "qs": "^6.1.0", + "readable-stream": "^2.0.5" } } } @@ -7332,12 +7878,12 @@ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", - "lodash": "4.17.4", + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", "slice-ansi": "0.0.4", - "string-width": "2.1.1" + "string-width": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -7358,8 +7904,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -7368,23 +7914,24 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } }, "tc-core-library-js": { "version": "github:appirio-tech/tc-core-library-js#df1f5c1a5578d3d1e475bfb4a7413d9dec25525a", - "requires": { - "auth0-js": "9.6.0", - "axios": "0.12.0", - "bunyan": "1.8.12", - "config": "1.27.0", - "jsonwebtoken": "7.4.3", - "jwks-rsa": "1.2.1", - "le_node": "1.7.1", - "lodash": "4.17.4", - "millisecond": "0.1.2" + "from": "github:appirio-tech/tc-core-library-js#v2.3", + "requires": { + "auth0-js": "^9.4.2", + "axios": "^0.12.0", + "bunyan": "^1.8.1", + "config": "^1.21.0", + "jsonwebtoken": "^7.0.0", + "jwks-rsa": "^1.2.1", + "le_node": "^1.3.1", + "lodash": "^4.13.1", + "millisecond": "^0.1.2" }, "dependencies": { "axios": { @@ -7400,8 +7947,8 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=", "requires": { - "debug": "2.6.9", - "stream-consume": "0.1.0" + "debug": "^2.2.0", + "stream-consume": "^0.1.0" } }, "hoek": { @@ -7419,10 +7966,10 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", "requires": { - "hoek": "2.16.3", - "isemail": "1.2.0", - "moment": "2.22.2", - "topo": "1.1.0" + "hoek": "2.x.x", + "isemail": "1.x.x", + "moment": "2.x.x", + "topo": "1.x.x" } }, "jsonwebtoken": { @@ -7430,11 +7977,11 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", "requires": { - "joi": "6.10.1", - "jws": "3.1.5", - "lodash.once": "4.1.1", - "ms": "2.0.0", - "xtend": "4.0.1" + "joi": "^6.10.1", + "jws": "^3.1.4", + "lodash.once": "^4.0.0", + "ms": "^2.0.0", + "xtend": "^4.0.1" } }, "topo": { @@ -7442,7 +7989,7 @@ "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } } } @@ -7453,7 +8000,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "0.7.0" + "execa": "^0.7.0" } }, "terraformer": { @@ -7461,7 +8008,7 @@ "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.8.tgz", "integrity": "sha1-UeCtiXRvzyFh3G9lqnDkI3fItZM=", "requires": { - "@types/geojson": "1.0.6" + "@types/geojson": "^1.0.0" } }, "terraformer-wkt-parser": { @@ -7469,7 +8016,7 @@ "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz", "integrity": "sha1-M2oMj8gglKWv+DKI9prt7NNpvww=", "requires": { - "terraformer": "1.0.8" + "terraformer": "~1.0.5" } }, "text-table": { @@ -7489,8 +8036,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" }, "dependencies": { "isarray": { @@ -7505,13 +8052,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -7520,7 +8067,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } } } @@ -7531,7 +8078,7 @@ "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.0" } }, "time-stamp": { @@ -7552,8 +8099,8 @@ "integrity": "sha1-YcxHp2wavTGV8UUn+XjViulMUgQ=", "dev": true, "requires": { - "es5-ext": "0.10.35", - "next-tick": "1.0.0" + "es5-ext": "~0.10.14", + "next-tick": "1" } }, "to-fast-properties": { @@ -7573,7 +8120,7 @@ "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } }, "toposort-class": { @@ -7587,7 +8134,7 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "1.0.10" + "nopt": "~1.0.10" }, "dependencies": { "nopt": { @@ -7596,7 +8143,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1.0.9" + "abbrev": "1" } } } @@ -7606,7 +8153,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" }, "dependencies": { "punycode": { @@ -7638,7 +8185,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -7653,7 +8200,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "type-detect": { @@ -7668,7 +8215,7 @@ "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" + "mime-types": "~2.1.15" } }, "typedarray": { @@ -7684,9 +8231,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" } }, "uglify-to-browserify": { @@ -7702,11 +8249,11 @@ "integrity": "sha1-p5yR8oYu7jEwxsNH8rkK1opm6Lg=", "dev": true, "requires": { - "bluebird": "3.5.1", - "lodash": "4.17.4", - "moment": "2.22.2", - "redefine": "0.2.1", - "resolve": "1.5.0" + "bluebird": "^3.4.1", + "lodash": "^4.17.0", + "moment": "^2.16.0", + "redefine": "^0.2.0", + "resolve": "^1.0.0" } }, "unc-path-regex": { @@ -7733,7 +8280,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "1.0.0" + "crypto-random-string": "^1.0.0" } }, "universalify": { @@ -7759,15 +8306,15 @@ "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=", "dev": true, "requires": { - "boxen": "1.2.2", - "chalk": "2.3.0", - "configstore": "3.1.1", - "import-lazy": "2.1.0", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" }, "dependencies": { "ansi-styles": { @@ -7776,7 +8323,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.0" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7785,9 +8332,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "has-flag": { @@ -7802,7 +8349,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -7827,7 +8374,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "1.0.4" + "prepend-http": "^1.0.1" } }, "urlencode": { @@ -7835,7 +8382,7 @@ "resolved": "https://registry.npmjs.org/urlencode/-/urlencode-1.1.0.tgz", "integrity": "sha1-HyuibwE8hfATP3o61v8nMK33y7c=", "requires": { - "iconv-lite": "0.4.19" + "iconv-lite": "~0.4.11" } }, "user-home": { @@ -7882,7 +8429,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "1.1.1" + "user-home": "^1.1.1" } }, "validate-npm-package-license": { @@ -7891,8 +8438,8 @@ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true, "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" + "spdx-correct": "~1.0.0", + "spdx-expression-parse": "~1.0.0" } }, "validator": { @@ -7910,9 +8457,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "very-fast-args": { @@ -7926,8 +8473,8 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "1.0.2", - "clone-stats": "0.0.1", + "clone": "^1.0.0", + "clone-stats": "^0.0.1", "replace-ext": "0.0.1" }, "dependencies": { @@ -7945,14 +8492,14 @@ "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", "dev": true, "requires": { - "defaults": "1.0.3", - "glob-stream": "3.1.18", - "glob-watcher": "0.0.6", - "graceful-fs": "3.0.11", - "mkdirp": "0.5.1", - "strip-bom": "1.0.0", - "through2": "0.6.5", - "vinyl": "0.4.6" + "defaults": "^1.0.0", + "glob-stream": "^3.1.5", + "glob-watcher": "^0.0.6", + "graceful-fs": "^3.0.0", + "mkdirp": "^0.5.0", + "strip-bom": "^1.0.0", + "through2": "^0.6.1", + "vinyl": "^0.4.0" }, "dependencies": { "clone": { @@ -7967,7 +8514,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "1.1.0" + "natives": "^1.1.0" } }, "readable-stream": { @@ -7976,10 +8523,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "strip-bom": { @@ -7988,8 +8535,8 @@ "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", "dev": true, "requires": { - "first-chunk-stream": "1.0.0", - "is-utf8": "0.2.1" + "first-chunk-stream": "^1.0.0", + "is-utf8": "^0.2.0" } }, "through2": { @@ -7998,8 +8545,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" } }, "vinyl": { @@ -8008,8 +8555,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "0.2.0", - "clone-stats": "0.0.1" + "clone": "^0.2.0", + "clone-stats": "^0.0.1" } } } @@ -8020,7 +8567,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -8035,7 +8582,7 @@ "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", "dev": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.1" } }, "winchan": { @@ -8067,8 +8614,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" } }, "wrappy": { @@ -8082,7 +8629,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } }, "write-file-atomic": { @@ -8091,9 +8638,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, "xdg-basedir": { @@ -8107,8 +8654,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "requires": { - "sax": "1.2.1", - "xmlbuilder": "4.2.1" + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" } }, "xmlbuilder": { @@ -8116,7 +8663,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "requires": { - "lodash": "4.17.4" + "lodash": "^4.0.0" } }, "xtend": { @@ -8142,9 +8689,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } }, @@ -8154,7 +8701,7 @@ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" }, "dependencies": { "camelcase": { diff --git a/src/app.js b/src/app.js index bd02c11c..8687aab5 100644 --- a/src/app.js +++ b/src/app.js @@ -6,6 +6,7 @@ import expressSanitizer from 'express-sanitizer'; import config from 'config'; import cors from 'cors'; import coreLib from 'tc-core-library-js'; +import performanceRequestLogger from './middlewares/performanceRequestLogger'; import expressRequestId from 'express-request-id'; import memWatch from 'memwatch-next'; import router from './routes'; @@ -62,7 +63,7 @@ const logger = coreLib.logger({ captureLogs: config.get('captureLogs'), logentriesToken: _.get(config, 'logentriesToken', null), }); -app.use(coreLib.middleware.logger(null, logger)); +app.use(performanceRequestLogger(logger)); app.logger = logger; // ======================== diff --git a/src/middlewares/performanceRequestLogger.js b/src/middlewares/performanceRequestLogger.js new file mode 100644 index 00000000..2b8f9d55 --- /dev/null +++ b/src/middlewares/performanceRequestLogger.js @@ -0,0 +1,37 @@ +import coreLib from 'tc-core-library-js'; + +module.exports = function logRequest(logger) { + if (!logger) { + throw new Error('Logger must be provided') + } + + // Use the logger from core lib for non-dev environment + if (process.env.NODE_ENV.toLowerCase() !== 'development') { + return coreLib.middleware.logger(null, logger); + } + + // Use the logger with memory usage info + return (req, res, next) => { + var startOpts = { + method: req.method, + url: req.url, + }; + // Create a per-request child + req.log = res.log = logger.child({ requestId: req.id }); + req.log.info('start request', startOpts); + const time = process.hrtime(); + res.on('finish', function responseSent() { + const diff = process.hrtime(time); + res.log.info('end request', { + method: startOpts.method, + url: startOpts.url, + statusCode: res.statusCode, + statusMessage: res.statusMessage, + duration: diff[0] * 1e3 + diff[1] * 1e-6, + heapUsed: process.memoryUsage().heapUsed + }); + }); + + next(); + }; +}; From cddec286cabd475f836abdaf2cfbc689678fc52a Mon Sep 17 00:00:00 2001 From: ngoctay Date: Thu, 30 Aug 2018 10:19:59 +0700 Subject: [PATCH 16/77] Revert utils.js#getTopcoderUser() --- src/util.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/util.js b/src/util.js index 34e12c7d..bc1fe325 100644 --- a/src/util.js +++ b/src/util.js @@ -307,19 +307,14 @@ _.assignIn(util, { httpClient.defaults.headers.common.Accept = 'application/json'; httpClient.defaults.headers.common['Content-Type'] = 'application/json'; httpClient.defaults.headers.common.Authorization = `Bearer ${jwtToken}`; - return httpClient.get(`${config.identityServiceEndpoint}users`, { - params: { - filter: `id=${userId}`, - }, - }) - .then((response) => { - if (response.data && response.data.result + return httpClient.get(`${config.identityServiceEndpoint}users/${userId}`).then((response) => { + if (response.data && response.data.result && response.data.result.status === 200 && response.data.result.content - && response.data.result.content.length === 1) { - return response.data.result.content[0]; - } - return null; - }); + && response.data.result.content.length > 0) { + return response.data.result.content[0]; + } + return null; + }); }, /** From e11ea6e69aacdf35c620962f2d96ddaacad96cc9 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 31 Aug 2018 15:52:55 +0530 Subject: [PATCH 17/77] lint fix --- src/app.js | 2 +- src/middlewares/performanceRequestLogger.js | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app.js b/src/app.js index 8687aab5..ff6d807c 100644 --- a/src/app.js +++ b/src/app.js @@ -6,9 +6,9 @@ import expressSanitizer from 'express-sanitizer'; import config from 'config'; import cors from 'cors'; import coreLib from 'tc-core-library-js'; -import performanceRequestLogger from './middlewares/performanceRequestLogger'; import expressRequestId from 'express-request-id'; import memWatch from 'memwatch-next'; +import performanceRequestLogger from './middlewares/performanceRequestLogger'; import router from './routes'; import permissions from './permissions'; import models from './models'; diff --git a/src/middlewares/performanceRequestLogger.js b/src/middlewares/performanceRequestLogger.js index 2b8f9d55..b0a32d8e 100644 --- a/src/middlewares/performanceRequestLogger.js +++ b/src/middlewares/performanceRequestLogger.js @@ -2,7 +2,7 @@ import coreLib from 'tc-core-library-js'; module.exports = function logRequest(logger) { if (!logger) { - throw new Error('Logger must be provided') + throw new Error('Logger must be provided'); } // Use the logger from core lib for non-dev environment @@ -12,23 +12,24 @@ module.exports = function logRequest(logger) { // Use the logger with memory usage info return (req, res, next) => { - var startOpts = { + const startOpts = { method: req.method, url: req.url, }; // Create a per-request child - req.log = res.log = logger.child({ requestId: req.id }); + req.log = logger.child({ requestId: req.id }); + res.log = req.log; req.log.info('start request', startOpts); const time = process.hrtime(); - res.on('finish', function responseSent() { + res.on('finish', () => { const diff = process.hrtime(time); res.log.info('end request', { method: startOpts.method, url: startOpts.url, statusCode: res.statusCode, statusMessage: res.statusMessage, - duration: diff[0] * 1e3 + diff[1] * 1e-6, - heapUsed: process.memoryUsage().heapUsed + duration: diff[0] * 1e3 + diff[1] * 1e-6, // eslint-disable-line no-mixed-operators + heapUsed: process.memoryUsage().heapUsed, }); }); From 63bb9b7281a557fa0c045b210134db840c52da6a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 31 Aug 2018 16:00:05 +0530 Subject: [PATCH 18/77] Column name fix --- migrations/20180824_milestone_templates_metadata.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/20180824_milestone_templates_metadata.sql b/migrations/20180824_milestone_templates_metadata.sql index 8962f98e..4aedb692 100644 --- a/migrations/20180824_milestone_templates_metadata.sql +++ b/migrations/20180824_milestone_templates_metadata.sql @@ -12,7 +12,7 @@ -- ALTER TABLE product_milestone_templates DROP CONSTRAINT "product_milestone_templates_productTemplateId_fkey"; -ALTER TABLE product_milestone_templates RENAME COLUMN "productTemplateId" TO referenceId; +ALTER TABLE product_milestone_templates RENAME COLUMN "productTemplateId" TO "referenceId"; ALTER TABLE product_milestone_templates ADD COLUMN "reference" character varying(45); UPDATE product_milestone_templates set reference='productTemplate' where reference is null; From c18df4bdac299f97ead0be3c9b94eb1b634568b2 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 31 Aug 2018 16:08:16 +0530 Subject: [PATCH 19/77] commented test for new auth scheme --- src/routes/projects/create.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/projects/create.spec.js b/src/routes/projects/create.spec.js index dcec4e55..f490d638 100644 --- a/src/routes/projects/create.spec.js +++ b/src/routes/projects/create.spec.js @@ -463,7 +463,7 @@ describe('Project create', () => { }); }); - it('should return 201 if valid user and data (using Bearer userId_)', (done) => { + xit('should return 201 if valid user and data (using Bearer userId_)', (done) => { const mockHttpClient = _.merge(testUtil.mockHttpClient, { post: () => Promise.resolve({ status: 200, From 442b5fe5ea629db3994c66c79aee2c5c3848725c Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 12:54:56 +0530 Subject: [PATCH 20/77] removed user id based auth for now as it is not needed any more for the new reg flow. Keeping the middleware though, in case we need it in future. --- src/routes/index.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/routes/index.js b/src/routes/index.js index c2a39ec8..aa7b793e 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -52,15 +52,6 @@ router.route('/v4/projects/metadata') router.all( RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), (req, res, next) => { - // userId authentication for project creation endpoint - if (req.method === 'POST' && - req.path.endsWith('/v4/projects') && - req.headers.authorization && - req.headers.authorization.startsWith('Bearer userId_') - ) { - return userIdAuth(req, res, next); - } - // JWT authentication return jwtAuth()(req, res, next); }, From 30495b49f9f9cae6e8ae497112d06ac62010549d Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 13:08:39 +0530 Subject: [PATCH 21/77] lint fixes --- src/routes/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/routes/index.js b/src/routes/index.js index aa7b793e..7dcba789 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -3,7 +3,6 @@ import _ from 'lodash'; import config from 'config'; import validate from 'express-validation'; import { Router } from 'express'; -import userIdAuth from '../middlewares/userIdAuth'; const router = Router(); @@ -51,10 +50,10 @@ router.route('/v4/projects/metadata') .get(require('./metadata/list')); router.all( - RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), (req, res, next) => { + RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), (req, res, next) => ( // JWT authentication - return jwtAuth()(req, res, next); - }, + jwtAuth()(req, res, next) + ), ); // Register all the routes From e329a65588acc0a5fd64141c87d6241ebc117b0b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 13:10:37 +0530 Subject: [PATCH 22/77] debug logs --- src/events/busApi.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/events/busApi.js b/src/events/busApi.js index 7e5167bf..6d9d2f26 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -497,6 +497,7 @@ module.exports = (app, logger) => { .then((project) => { sendMilestoneNotification(req, original, updated, project); + logger.debug('cascadedUpdates', cascadedUpdates); _.each(cascadedUpdates, cascadedUpdate => sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project), ); From abbdb9385ed0c71192e50f60704e011b202badf0 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 14:11:33 +0530 Subject: [PATCH 23/77] updated package lock with more specific versions of libraries instead of using relaxed versions mappings. --- package-lock.json | 3033 ++++++++++++++++++++++----------------------- 1 file changed, 1516 insertions(+), 1517 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccc64298..72ec9d9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-1.1.2.tgz", "integrity": "sha1-13hAmZ4/fkPnSzsNQzkcFSb3k7g=", "requires": { - "component-type": "^1.2.1", - "join-component": "^1.1.0" + "component-type": "1.2.1", + "join-component": "1.1.0" } }, "@types/body-parser": { @@ -18,8 +18,8 @@ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.16.8.tgz", "integrity": "sha512-BdN2PXxOFnTXFcyONPW6t0fHjz2fvRZHVMFpaS0wYr+Y8fWEaNOs4V8LEu/fpzQlMx+ahdndgTaGTwPC+J/EeA==", "requires": { - "@types/express": "*", - "@types/node": "*" + "@types/express": "4.0.39", + "@types/node": "8.5.1" } }, "@types/express": { @@ -27,9 +27,9 @@ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.39.tgz", "integrity": "sha512-dBUam7jEjyuEofigUXCtublUHknRZvcRgITlGsTbFgPvnTwtQUt2NgLakbsf+PsGo/Nupqr3IXCYsOpBpofyrA==", "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" + "@types/body-parser": "1.16.8", + "@types/express-serve-static-core": "4.0.57", + "@types/serve-static": "1.13.1" } }, "@types/express-jwt": { @@ -37,8 +37,8 @@ "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.34.tgz", "integrity": "sha1-/b7kxq9cCiRu8qkz9VGZc8dxfwI=", "requires": { - "@types/express": "*", - "@types/express-unless": "*" + "@types/express": "4.0.39", + "@types/express-unless": "0.0.32" } }, "@types/express-serve-static-core": { @@ -46,7 +46,7 @@ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.0.57.tgz", "integrity": "sha512-QLAHjdLwEICm3thVbXSKRoisjfgMVI4xJH/HU8F385BR2HI7PmM6ax4ELXf8Du6sLmSpySXMYaI+xc//oQ/IFw==", "requires": { - "@types/node": "*" + "@types/node": "8.5.1" } }, "@types/express-unless": { @@ -54,7 +54,7 @@ "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.0.32.tgz", "integrity": "sha512-6YpJyFNlDDnPnRjMOvJCoDYlSDDmG/OEEUsPk7yhNkL4G9hUYtgab6vi1CcWsGSSSM0CsvNlWTG+ywAGnvF03g==", "requires": { - "@types/express": "*" + "@types/express": "4.0.39" } }, "@types/geojson": { @@ -77,8 +77,8 @@ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.1.tgz", "integrity": "sha512-jDMH+3BQPtvqZVIcsH700Dfi8Q3MIcEx16g/VdxjoqiGR/NntekB10xdBpirMKnPe9z2C5cBmL0vte0YttOr3Q==", "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" + "@types/express-serve-static-core": "4.0.57", + "@types/mime": "2.0.0" } }, "abbrev": { @@ -92,7 +92,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", "requires": { - "mime-types": "~2.1.16", + "mime-types": "2.1.17", "negotiator": "0.6.1" } }, @@ -108,7 +108,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "^3.0.4" + "acorn": "3.3.0" }, "dependencies": { "acorn": { @@ -125,8 +125,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ajv-keywords": { @@ -141,9 +141,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -157,10 +157,10 @@ "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.1.tgz", "integrity": "sha1-fMz+ur5WwumE6noiQ/fO/m+/xs8=", "requires": { - "bitsyntax": "~0.0.4", - "bluebird": "^3.4.6", + "bitsyntax": "0.0.4", + "bluebird": "3.5.1", "buffer-more-ints": "0.0.2", - "readable-stream": "1.x >=1.1.9" + "readable-stream": "1.1.14" } }, "analytics-node": { @@ -168,15 +168,15 @@ "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-2.4.1.tgz", "integrity": "sha1-H5bI64h7bEdpEESsf8mhIx+wIPc=", "requires": { - "@segment/loosely-validate-event": "^1.1.2", - "clone": "^2.1.1", - "commander": "^2.9.0", - "crypto-token": "^1.0.1", - "debug": "^2.6.2", - "lodash": "^4.17.4", - "remove-trailing-slash": "^0.1.0", - "superagent": "^3.5.0", - "superagent-retry": "^0.6.0" + "@segment/loosely-validate-event": "1.1.2", + "clone": "2.1.1", + "commander": "2.11.0", + "crypto-token": "1.0.1", + "debug": "2.6.9", + "lodash": "4.17.4", + "remove-trailing-slash": "0.1.0", + "superagent": "3.8.0", + "superagent-retry": "0.6.0" } }, "ansi-align": { @@ -185,7 +185,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -206,8 +206,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -216,7 +216,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -243,8 +243,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "app-module-path": { @@ -258,7 +258,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "^1.0.0" + "default-require-extensions": "1.0.0" } }, "archy": { @@ -273,7 +273,7 @@ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -282,7 +282,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "arr-flatten": { @@ -320,7 +320,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -379,8 +379,8 @@ "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.8.tgz", "integrity": "sha512-1Sy1jDhjlgxcSd9/ICHqiAHT8VSJ9R1lzEyWwP/4Hm9p8nVTNtU0SxG/Z15XHD/aZvQraSw9BpDU3EBcFnOVrw==", "requires": { - "semver": "^5.3.0", - "shimmer": "^1.1.0" + "semver": "5.4.1", + "shimmer": "1.1.0" } }, "asynckit": { @@ -393,13 +393,13 @@ "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.6.0.tgz", "integrity": "sha1-2a4wFIBzZtO0ecKtGKNTfz4Mlpk=", "requires": { - "base64-js": "^1.2.0", - "idtoken-verifier": "^1.2.0", - "js-cookie": "^2.2.0", - "qs": "^6.4.0", - "superagent": "^3.8.2", - "url-join": "^1.1.0", - "winchan": "^0.2.0" + "base64-js": "1.2.1", + "idtoken-verifier": "1.2.0", + "js-cookie": "2.2.0", + "qs": "6.5.1", + "superagent": "3.8.3", + "url-join": "1.1.0", + "winchan": "0.2.0" }, "dependencies": { "debug": { @@ -430,13 +430,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -444,7 +444,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "superagent": { @@ -452,16 +452,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.1", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.6" } } } @@ -473,7 +473,7 @@ "requires": { "buffer": "4.9.1", "crypto-browserify": "1.0.9", - "events": "^1.1.1", + "events": "1.1.1", "jmespath": "0.15.0", "querystring": "0.2.0", "sax": "1.2.1", @@ -498,8 +498,8 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", "requires": { - "follow-redirects": "^1.2.5", - "is-buffer": "^1.1.5" + "follow-redirects": "1.2.6", + "is-buffer": "1.1.6" } }, "babel-cli": { @@ -508,21 +508,21 @@ "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-polyfill": "^6.26.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "chokidar": "^1.6.1", - "commander": "^2.11.0", - "convert-source-map": "^1.5.0", - "fs-readdir-recursive": "^1.0.0", - "glob": "^7.1.2", - "lodash": "^4.17.4", - "output-file-sync": "^1.1.2", - "path-is-absolute": "^1.0.1", - "slash": "^1.0.0", - "source-map": "^0.5.6", - "v8flags": "^2.1.1" + "babel-core": "6.26.0", + "babel-polyfill": "6.26.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "chokidar": "1.7.0", + "commander": "2.11.0", + "convert-source-map": "1.5.0", + "fs-readdir-recursive": "1.0.0", + "glob": "7.1.2", + "lodash": "4.17.4", + "output-file-sync": "1.1.2", + "path-is-absolute": "1.0.1", + "slash": "1.0.0", + "source-map": "0.5.7", + "v8flags": "2.1.1" }, "dependencies": { "babel-runtime": { @@ -531,8 +531,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } }, "glob": { @@ -541,12 +541,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } @@ -557,9 +557,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-core": { @@ -568,25 +568,25 @@ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.0", - "debug": "^2.6.8", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.7", - "slash": "^1.0.0", - "source-map": "^0.5.6" + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.0", + "babel-helpers": "6.24.1", + "babel-messages": "6.23.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "convert-source-map": "1.5.0", + "debug": "2.6.9", + "json5": "0.5.1", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "private": "0.1.8", + "slash": "1.0.0", + "source-map": "0.5.7" }, "dependencies": { "babel-runtime": { @@ -595,8 +595,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } }, "json5": { @@ -613,10 +613,10 @@ "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "babel-traverse": "^6.23.1", - "babel-types": "^6.23.0", - "babylon": "^6.17.0" + "babel-code-frame": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0" } }, "babel-generator": { @@ -625,14 +625,14 @@ "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.6", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { "babel-runtime": { @@ -641,8 +641,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -653,10 +653,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -665,8 +665,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -677,10 +677,10 @@ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.4" }, "dependencies": { "babel-runtime": { @@ -689,8 +689,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -701,11 +701,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -714,8 +714,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -726,8 +726,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -736,8 +736,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -748,8 +748,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -758,8 +758,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -770,8 +770,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -780,8 +780,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -792,9 +792,9 @@ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.4" }, "dependencies": { "babel-runtime": { @@ -803,8 +803,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -815,12 +815,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -829,8 +829,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -841,8 +841,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -851,8 +851,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -863,7 +863,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -872,8 +872,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -890,7 +890,7 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -899,8 +899,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -911,7 +911,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -920,8 +920,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -932,7 +932,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -941,8 +941,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -953,11 +953,11 @@ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.4" }, "dependencies": { "babel-runtime": { @@ -966,8 +966,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -978,15 +978,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-define-map": "6.26.0", + "babel-helper-function-name": "6.24.1", + "babel-helper-optimise-call-expression": "6.24.1", + "babel-helper-replace-supers": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -995,8 +995,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1007,8 +1007,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1017,8 +1017,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1029,7 +1029,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1038,8 +1038,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1050,8 +1050,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1060,8 +1060,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1072,7 +1072,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1081,8 +1081,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1093,9 +1093,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1104,8 +1104,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1116,7 +1116,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1125,8 +1125,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1137,9 +1137,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1148,8 +1148,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1160,10 +1160,10 @@ "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1172,8 +1172,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1184,9 +1184,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1195,8 +1195,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1207,9 +1207,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1218,8 +1218,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1230,8 +1230,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1240,8 +1240,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1252,12 +1252,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1266,8 +1266,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1278,8 +1278,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1288,8 +1288,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1300,7 +1300,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1309,8 +1309,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1321,9 +1321,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1332,8 +1332,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1344,7 +1344,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1353,8 +1353,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1365,7 +1365,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1374,8 +1374,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1386,9 +1386,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "regexpu-core": "2.0.0" }, "dependencies": { "babel-runtime": { @@ -1397,8 +1397,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1409,7 +1409,7 @@ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { - "regenerator-transform": "^0.10.0" + "regenerator-transform": "0.10.1" } }, "babel-plugin-transform-runtime": { @@ -1418,7 +1418,7 @@ "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1427,8 +1427,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1439,8 +1439,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" }, "dependencies": { "babel-runtime": { @@ -1449,8 +1449,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1461,9 +1461,9 @@ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" + "babel-runtime": "6.26.0", + "core-js": "2.5.1", + "regenerator-runtime": "0.10.5" }, "dependencies": { "babel-runtime": { @@ -1472,8 +1472,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" }, "dependencies": { "regenerator-runtime": { @@ -1498,30 +1498,30 @@ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0" } }, "babel-register": { @@ -1530,13 +1530,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" + "babel-core": "6.26.0", + "babel-runtime": "6.26.0", + "core-js": "2.5.1", + "home-or-tmp": "2.0.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18" }, "dependencies": { "babel-runtime": { @@ -1545,8 +1545,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1556,7 +1556,7 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", "integrity": "sha1-eIuUtvY04luRvWxd9y1GdFevsAA=", "requires": { - "core-js": "^2.1.0" + "core-js": "2.5.1" } }, "babel-template": { @@ -1565,11 +1565,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" }, "dependencies": { "babel-runtime": { @@ -1578,8 +1578,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1590,15 +1590,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" }, "dependencies": { "babel-runtime": { @@ -1607,8 +1607,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1619,10 +1619,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" }, "dependencies": { "babel-runtime": { @@ -1631,8 +1631,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -1648,7 +1648,7 @@ "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", "requires": { - "precond": "0.2" + "precond": "0.2.3" } }, "balanced-match": { @@ -1667,7 +1667,7 @@ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "beeper": { @@ -1706,15 +1706,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", + "depd": "1.1.1", + "http-errors": "1.6.2", "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "~1.6.15" + "type-is": "1.6.15" } }, "boom": { @@ -1722,7 +1722,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.0" } }, "boxen": { @@ -1731,13 +1731,13 @@ "integrity": "sha1-Px1AMsMP/qnUsCwyLq8up0HcvOU=", "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^1.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.3.0", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "1.0.0" }, "dependencies": { "ansi-regex": { @@ -1752,7 +1752,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "camelcase": { @@ -1767,9 +1767,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" } }, "has-flag": { @@ -1790,8 +1790,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -1800,7 +1800,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { @@ -1809,7 +1809,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -1819,7 +1819,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1829,9 +1829,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "buffer": { @@ -1839,9 +1839,9 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.2.1", + "ieee754": "1.1.8", + "isarray": "1.0.0" }, "dependencies": { "isarray": { @@ -1877,10 +1877,10 @@ "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "requires": { - "dtrace-provider": "~0.8", - "moment": "^2.10.6", - "mv": "~2", - "safe-json-stringify": "~1" + "dtrace-provider": "0.8.5", + "moment": "2.22.2", + "mv": "2.1.1", + "safe-json-stringify": "1.0.4" } }, "bytes": { @@ -1894,7 +1894,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsites": { @@ -1928,8 +1928,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chai": { @@ -1938,9 +1938,9 @@ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" + "assertion-error": "1.0.2", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" } }, "chalk": { @@ -1948,11 +1948,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "check-more-types": { @@ -1967,15 +1967,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.2.4", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" } }, "circular-json": { @@ -1996,12 +1996,12 @@ "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=", "dev": true, "requires": { - "ansi-regex": "^2.1.1", - "d": "1", - "es5-ext": "^0.10.12", - "es6-iterator": "2", - "memoizee": "^0.4.3", - "timers-ext": "0.1" + "ansi-regex": "2.1.1", + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", + "memoizee": "0.4.11", + "timers-ext": "0.1.2" } }, "cli-cursor": { @@ -2010,7 +2010,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "^1.0.1" + "restore-cursor": "1.0.1" } }, "cli-width": { @@ -2026,8 +2026,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -2083,7 +2083,7 @@ "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -2102,7 +2102,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -2131,9 +2131,9 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" }, "dependencies": { "isarray": { @@ -2148,13 +2148,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -2163,7 +2163,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -2183,8 +2183,8 @@ "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", "dev": true, "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "ini": "1.3.4", + "proto-list": "1.2.4" } }, "configstore": { @@ -2193,12 +2193,12 @@ "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.1.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" } }, "contains-path": { @@ -2222,8 +2222,8 @@ "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.0.tgz", "integrity": "sha1-4Z/Da1lwkKXU5KOy6j68XilpSiQ=", "requires": { - "async-listener": "^0.6.0", - "emitter-listener": "^1.0.1" + "async-listener": "0.6.8", + "emitter-listener": "1.0.1" } }, "convert-source-map": { @@ -2262,8 +2262,8 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", "requires": { - "object-assign": "^4", - "vary": "^1" + "object-assign": "4.1.1", + "vary": "1.1.2" } }, "create-error-class": { @@ -2272,7 +2272,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "cross-spawn": { @@ -2281,9 +2281,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" }, "dependencies": { "lru-cache": { @@ -2292,8 +2292,8 @@ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } } } @@ -2303,7 +2303,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "boom": "5.x.x" + "boom": "5.2.0" }, "dependencies": { "boom": { @@ -2311,7 +2311,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.0" } } } @@ -2343,7 +2343,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.35" } }, "dashdash": { @@ -2351,7 +2351,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "dateformat": { @@ -2409,7 +2409,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "^2.0.0" + "strip-bom": "2.0.0" }, "dependencies": { "strip-bom": { @@ -2418,7 +2418,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -2429,7 +2429,7 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.2" }, "dependencies": { "clone": { @@ -2446,13 +2446,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.4.5" } }, "delayed-stream": { @@ -2482,7 +2482,7 @@ "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", "dev": true, "requires": { - "fs-exists-sync": "^0.1.0" + "fs-exists-sync": "0.1.0" } }, "detect-indent": { @@ -2491,7 +2491,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "diff": { @@ -2506,8 +2506,8 @@ "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" }, "dependencies": { "isarray": { @@ -2524,7 +2524,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "dottie": { @@ -2538,7 +2538,7 @@ "integrity": "sha1-mOu6Ihr6xG4cOf02hY2Pk2dSS5I=", "optional": true, "requires": { - "nan": "^2.3.3" + "nan": "2.7.0" } }, "duplexer": { @@ -2553,7 +2553,7 @@ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "dev": true, "requires": { - "readable-stream": "~1.1.9" + "readable-stream": "1.1.14" } }, "duplexer3": { @@ -2568,7 +2568,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "ecdsa-sig-formatter": { @@ -2576,7 +2576,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "editorconfig": { @@ -2585,11 +2585,11 @@ "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==", "dev": true, "requires": { - "bluebird": "^3.0.5", - "commander": "^2.9.0", - "lru-cache": "^3.2.0", - "semver": "^5.1.0", - "sigmund": "^1.0.1" + "bluebird": "3.5.1", + "commander": "2.11.0", + "lru-cache": "3.2.0", + "semver": "5.4.1", + "sigmund": "1.0.1" }, "dependencies": { "lru-cache": { @@ -2598,7 +2598,7 @@ "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", "dev": true, "requires": { - "pseudomap": "^1.0.1" + "pseudomap": "1.0.2" } } } @@ -2613,11 +2613,11 @@ "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-11.0.1.tgz", "integrity": "sha1-0YBoTGvefs+g+iTmL6HIcu6uCOc=", "requires": { - "chalk": "^1.0.0", - "forever-agent": "^0.6.0", - "lodash": "^3.10.0", - "lodash-compat": "^3.0.0", - "promise": "^7.1.1" + "chalk": "1.1.3", + "forever-agent": "0.6.1", + "lodash": "3.10.1", + "lodash-compat": "3.10.2", + "promise": "7.3.1" }, "dependencies": { "lodash": { @@ -2653,7 +2653,7 @@ "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "dev": true, "requires": { - "once": "~1.3.0" + "once": "1.3.3" }, "dependencies": { "once": { @@ -2662,7 +2662,7 @@ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } } } @@ -2673,7 +2673,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es5-ext": { @@ -2682,8 +2682,8 @@ "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=", "dev": true, "requires": { - "es6-iterator": "~2.0.1", - "es6-symbol": "~3.1.1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "es6-iterator": { @@ -2692,9 +2692,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-symbol": "3.1.1" } }, "es6-map": { @@ -2703,12 +2703,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" } }, "es6-promise": { @@ -2723,11 +2723,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" } }, "es6-symbol": { @@ -2736,8 +2736,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.35" } }, "es6-weak-map": { @@ -2746,10 +2746,10 @@ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "escape-html": { @@ -2768,10 +2768,10 @@ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.0", + "estraverse": "4.2.0" } }, "eslint": { @@ -2780,41 +2780,41 @@ "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "dev": true, "requires": { - "babel-code-frame": "^6.16.0", - "chalk": "^1.1.3", - "concat-stream": "^1.5.2", - "debug": "^2.1.1", - "doctrine": "^2.0.0", - "escope": "^3.6.0", - "espree": "^3.4.0", - "esquery": "^1.0.0", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "glob": "^7.0.3", - "globals": "^9.14.0", - "ignore": "^3.2.0", - "imurmurhash": "^0.1.4", - "inquirer": "^0.12.0", - "is-my-json-valid": "^2.10.0", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.5.1", - "json-stable-stringify": "^1.0.0", - "levn": "^0.3.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.0", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.1", - "pluralize": "^1.2.1", - "progress": "^1.1.8", - "require-uncached": "^1.0.2", - "shelljs": "^0.7.5", - "strip-bom": "^3.0.0", - "strip-json-comments": "~2.0.1", - "table": "^3.7.8", - "text-table": "~0.2.0", - "user-home": "^2.0.0" + "babel-code-frame": "6.26.0", + "chalk": "1.1.3", + "concat-stream": "1.6.0", + "debug": "2.6.9", + "doctrine": "2.0.0", + "escope": "3.6.0", + "espree": "3.5.1", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.16.1", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.7.8", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" }, "dependencies": { "glob": { @@ -2823,12 +2823,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "user-home": { @@ -2837,7 +2837,7 @@ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } } } @@ -2848,7 +2848,7 @@ "integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==", "dev": true, "requires": { - "eslint-restricted-globals": "^0.1.1" + "eslint-restricted-globals": "0.1.1" } }, "eslint-import-resolver-node": { @@ -2857,8 +2857,8 @@ "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", "dev": true, "requires": { - "debug": "^2.6.8", - "resolve": "^1.2.0" + "debug": "2.6.9", + "resolve": "1.5.0" } }, "eslint-module-utils": { @@ -2867,8 +2867,8 @@ "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", "dev": true, "requires": { - "debug": "^2.6.8", - "pkg-dir": "^1.0.0" + "debug": "2.6.9", + "pkg-dir": "1.0.0" } }, "eslint-plugin-import": { @@ -2877,16 +2877,16 @@ "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", "dev": true, "requires": { - "builtin-modules": "^1.1.1", - "contains-path": "^0.1.0", - "debug": "^2.6.8", + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.1.1", - "has": "^1.0.1", - "lodash.cond": "^4.3.0", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0" + "eslint-import-resolver-node": "0.3.1", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "read-pkg-up": "2.0.0" }, "dependencies": { "doctrine": { @@ -2895,8 +2895,8 @@ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } }, "isarray": { @@ -2919,8 +2919,8 @@ "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", "dev": true, "requires": { - "acorn": "^5.1.1", - "acorn-jsx": "^3.0.0" + "acorn": "5.2.1", + "acorn-jsx": "3.0.1" } }, "esprima": { @@ -2935,7 +2935,7 @@ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -2944,8 +2944,8 @@ "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", "dev": true, "requires": { - "estraverse": "^4.1.0", - "object-assign": "^4.0.1" + "estraverse": "4.2.0", + "object-assign": "4.1.1" } }, "estraverse": { @@ -2971,8 +2971,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.35" } }, "event-stream": { @@ -2981,13 +2981,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "events": { @@ -3001,13 +3001,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "exit-hook": { @@ -3022,7 +3022,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -3031,7 +3031,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" } }, "expand-tilde": { @@ -3040,7 +3040,7 @@ "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", "dev": true, "requires": { - "os-homedir": "^1.0.1" + "os-homedir": "1.0.2" } }, "express": { @@ -3048,36 +3048,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.4", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "~1.0.4", + "content-type": "1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.1", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.2", + "proxy-addr": "2.0.2", "qs": "6.5.1", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "safe-buffer": "5.1.1", "send": "0.16.1", "serve-static": "1.13.1", "setprototypeof": "1.1.0", - "statuses": "~1.3.1", - "type-is": "~1.6.15", + "statuses": "1.3.1", + "type-is": "1.6.15", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "setprototypeof": { @@ -3097,8 +3097,8 @@ "resolved": "https://registry.npmjs.org/express-list-routes/-/express-list-routes-0.1.4.tgz", "integrity": "sha1-xlwxw/thnHnAVD97TsToMFbs5hY=", "requires": { - "colors": "^1.0.3", - "lodash": "^3.0.0" + "colors": "1.1.2", + "lodash": "3.10.1" }, "dependencies": { "lodash": { @@ -3113,7 +3113,7 @@ "resolved": "https://registry.npmjs.org/express-request-id/-/express-request-id-1.4.0.tgz", "integrity": "sha1-J3ssCUmAPmgQTJ1Fw+aJNPlr9aI=", "requires": { - "uuid": "^3.0.1" + "uuid": "3.1.0" } }, "express-sanitizer": { @@ -3129,7 +3129,7 @@ "resolved": "https://registry.npmjs.org/express-validation/-/express-validation-0.6.0.tgz", "integrity": "sha1-DXf0r8flixIBat7FmzJb7v2dwmg=", "requires": { - "lodash": "^4.9.0" + "lodash": "4.17.4" } }, "extend": { @@ -3143,7 +3143,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "extsprintf": { @@ -3157,8 +3157,8 @@ "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", "dev": true, "requires": { - "chalk": "^1.1.1", - "time-stamp": "^1.0.0" + "chalk": "1.1.3", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -3183,8 +3183,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "file-entry-cache": { @@ -3193,8 +3193,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.0", + "object-assign": "4.1.1" } }, "filename-regex": { @@ -3209,8 +3209,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "glob": "7.1.2", + "minimatch": "3.0.4" }, "dependencies": { "glob": { @@ -3219,12 +3219,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } @@ -3235,11 +3235,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "finalhandler": { @@ -3248,12 +3248,12 @@ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" }, "dependencies": { "statuses": { @@ -3275,8 +3275,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "findup-sync": { @@ -3285,10 +3285,10 @@ "integrity": "sha1-b35LV7buOkA3tEFOrt6j9Y9x4Ow=", "dev": true, "requires": { - "detect-file": "^0.1.0", - "is-glob": "^2.0.1", - "micromatch": "^2.3.7", - "resolve-dir": "^0.1.0" + "detect-file": "0.1.0", + "is-glob": "2.0.1", + "micromatch": "2.3.11", + "resolve-dir": "0.1.1" } }, "fined": { @@ -3297,11 +3297,11 @@ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.1" }, "dependencies": { "expand-tilde": { @@ -3310,7 +3310,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } } } @@ -3333,10 +3333,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" } }, "follow-redirects": { @@ -3344,7 +3344,7 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.6.tgz", "integrity": "sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA==", "requires": { - "debug": "^3.1.0" + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -3369,7 +3369,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "forever-agent": { @@ -3382,9 +3382,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" } }, "formatio": { @@ -3393,7 +3393,7 @@ "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", "dev": true, "requires": { - "samsam": "~1.1" + "samsam": "1.1.2" } }, "formidable": { @@ -3429,9 +3429,9 @@ "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" } }, "fs-readdir-recursive": { @@ -3453,8 +3453,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.11.0", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -3480,8 +3480,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { @@ -3494,7 +3494,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -3558,7 +3558,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "fs.realpath": { @@ -3573,14 +3573,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" } }, "glob": { @@ -3589,12 +3589,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -3609,7 +3609,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -3618,7 +3618,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -3627,8 +3627,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -3647,7 +3647,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -3661,7 +3661,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -3674,8 +3674,8 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "minizlib": { @@ -3684,7 +3684,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "mkdirp": { @@ -3714,9 +3714,9 @@ "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -3725,16 +3725,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" } }, "nopt": { @@ -3743,8 +3743,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -3759,8 +3759,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { @@ -3769,10 +3769,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -3791,7 +3791,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -3812,8 +3812,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -3834,10 +3834,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -3854,13 +3854,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "rimraf": { @@ -3869,7 +3869,7 @@ "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-buffer": { @@ -3907,23 +3907,23 @@ "dev": true, "optional": true }, - "string-width": { - "version": "1.0.2", + "string_decoder": { + "version": "1.1.1", "bundled": true, "dev": true, + "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "safe-buffer": "5.1.1" } }, - "string_decoder": { - "version": "1.1.1", + "string-width": { + "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "strip-ansi": { @@ -3931,7 +3931,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -3946,13 +3946,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "util-deprecate": { @@ -3967,7 +3967,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -3994,7 +3994,7 @@ "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "dev": true, "requires": { - "globule": "~0.1.0" + "globule": "0.1.0" } }, "generate-function": { @@ -4009,7 +4009,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "^1.0.0" + "is-property": "1.0.2" } }, "generic-pool": { @@ -4034,7 +4034,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { @@ -4042,11 +4042,11 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -4055,8 +4055,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -4065,7 +4065,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "glob-stream": { @@ -4074,12 +4074,12 @@ "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "dev": true, "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" }, "dependencies": { "glob": { @@ -4088,10 +4088,10 @@ "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.4.0" } }, "minimatch": { @@ -4100,7 +4100,7 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "1.1.8" } }, "readable-stream": { @@ -4109,10 +4109,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "through2": { @@ -4121,8 +4121,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -4133,7 +4133,7 @@ "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "dev": true, "requires": { - "gaze": "^0.5.1" + "gaze": "0.5.2" } }, "glob2base": { @@ -4142,7 +4142,7 @@ "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", "dev": true, "requires": { - "find-index": "^0.1.1" + "find-index": "0.1.1" } }, "global-dirs": { @@ -4151,7 +4151,7 @@ "integrity": "sha1-ENNAOeDfBCcuJizyQiT3IJQ0308=", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.4" } }, "global-modules": { @@ -4160,8 +4160,8 @@ "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", "dev": true, "requires": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" + "global-prefix": "0.1.5", + "is-windows": "0.2.0" } }, "global-prefix": { @@ -4170,10 +4170,10 @@ "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" + "homedir-polyfill": "1.0.1", + "ini": "1.3.4", + "is-windows": "0.2.0", + "which": "1.3.0" } }, "globals": { @@ -4188,12 +4188,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" }, "dependencies": { "glob": { @@ -4202,12 +4202,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } @@ -4218,9 +4218,9 @@ "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "dev": true, "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" }, "dependencies": { "glob": { @@ -4229,9 +4229,9 @@ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "dev": true, "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" } }, "graceful-fs": { @@ -4258,8 +4258,8 @@ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "dev": true, "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "lru-cache": "2.7.3", + "sigmund": "1.0.1" } } } @@ -4270,7 +4270,7 @@ "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.0" } }, "got": { @@ -4279,17 +4279,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.0", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -4310,19 +4310,19 @@ "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.0.4", + "liftoff": "2.3.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" }, "dependencies": { "minimist": { @@ -4345,8 +4345,8 @@ "integrity": "sha1-Jh2xhuGDl/7z9qLCLpwxW/qIrgw=", "dev": true, "requires": { - "chalk": "^1.0.0", - "object-assign": "^3.0.0" + "chalk": "1.1.3", + "object-assign": "3.0.0" }, "dependencies": { "object-assign": { @@ -4363,24 +4363,24 @@ "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "dev": true, "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "through2": "2.0.3", + "vinyl": "0.5.3" }, "dependencies": { "minimist": { @@ -4403,7 +4403,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.0" } }, "handlebars": { @@ -4412,10 +4412,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -4424,7 +4424,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -4439,8 +4439,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ajv": "5.5.1", + "har-schema": "2.0.0" }, "dependencies": { "ajv": { @@ -4448,10 +4448,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } } } @@ -4462,7 +4462,7 @@ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "dev": true, "requires": { - "function-bind": "^1.0.2" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -4470,7 +4470,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -4485,7 +4485,7 @@ "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.0" } }, "hawk": { @@ -4493,10 +4493,10 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.1.0" } }, "hoek": { @@ -4510,8 +4510,8 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "homedir-polyfill": { @@ -4520,7 +4520,7 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hosted-git-info": { @@ -4534,7 +4534,7 @@ "resolved": "https://registry.npmjs.org/http-aws-es/-/http-aws-es-1.1.3.tgz", "integrity": "sha1-ZJUYQ7XFETBQclcNfCxQn3gUTWs=", "requires": { - "aws-sdk": "^2.2.19" + "aws-sdk": "2.143.0" } }, "http-errors": { @@ -4545,7 +4545,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "statuses": "1.4.0" } }, "http-signature": { @@ -4553,9 +4553,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" } }, "iconv-lite": { @@ -4568,11 +4568,11 @@ "resolved": "https://registry.npmjs.org/idtoken-verifier/-/idtoken-verifier-1.2.0.tgz", "integrity": "sha512-8jmmFHwdPz8L73zGNAXHHOV9yXNC+Z0TUBN5rafpoaFaLFltlIFr1JkQa3FYAETP23eSsulVw0sBiwrE8jqbUg==", "requires": { - "base64-js": "^1.2.0", - "crypto-js": "^3.1.9-1", - "jsbn": "^0.1.0", - "superagent": "^3.8.2", - "url-join": "^1.1.0" + "base64-js": "1.2.1", + "crypto-js": "3.1.9-1", + "jsbn": "0.1.1", + "superagent": "3.8.3", + "url-join": "1.1.0" }, "dependencies": { "debug": { @@ -4603,13 +4603,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -4617,7 +4617,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "superagent": { @@ -4625,16 +4625,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.1", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.6" } } } @@ -4678,8 +4678,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -4699,19 +4699,19 @@ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.17.4", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" } }, "interpret": { @@ -4726,7 +4726,7 @@ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -4746,8 +4746,8 @@ "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", "dev": true, "requires": { - "is-relative": "^0.2.1", - "is-windows": "^0.2.0" + "is-relative": "0.2.1", + "is-windows": "0.2.0" } }, "is-arrayish": { @@ -4762,7 +4762,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.10.0" } }, "is-buffer": { @@ -4776,7 +4776,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-dotfile": { @@ -4791,7 +4791,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -4812,7 +4812,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -4821,7 +4821,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-glob": { @@ -4830,7 +4830,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-installed-globally": { @@ -4839,8 +4839,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.0", + "is-path-inside": "1.0.0" } }, "is-my-json-valid": { @@ -4849,10 +4849,10 @@ "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", "dev": true, "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-npm": { @@ -4867,7 +4867,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-obj": { @@ -4888,7 +4888,7 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.0" } }, "is-path-inside": { @@ -4897,7 +4897,7 @@ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-object": { @@ -4906,7 +4906,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -4953,7 +4953,7 @@ "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", "dev": true, "requires": { - "is-unc-path": "^0.1.1" + "is-unc-path": "0.1.2" } }, "is-resolvable": { @@ -4962,7 +4962,7 @@ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", "dev": true, "requires": { - "tryit": "^1.0.1" + "tryit": "1.0.3" } }, "is-retry-allowed": { @@ -4988,7 +4988,7 @@ "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", "dev": true, "requires": { - "unc-path-regex": "^0.1.0" + "unc-path-regex": "0.1.2" } }, "is-utf8": { @@ -5047,14 +5047,14 @@ "integrity": "sha1-BglrwI6Yuq10Sq5Gli2N+frGPQg=", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "istanbul-api": "^1.0.0-alpha", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "abbrev": "1.0.9", + "async": "1.5.2", + "istanbul-api": "1.2.1", + "js-yaml": "3.10.0", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "which": "1.3.0", + "wordwrap": "1.0.0" } }, "istanbul-api": { @@ -5063,17 +5063,17 @@ "integrity": "sha512-oFCwXvd65amgaPCzqrR+a2XjanS1MvpXN6l/MlMUTv6uiA1NOgGX+I0uyq8Lg3GDxsxPsaP1049krz3hIJ5+KA==", "dev": true, "requires": { - "async": "^2.1.4", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.1.1", - "istanbul-lib-hook": "^1.1.0", - "istanbul-lib-instrument": "^1.9.1", - "istanbul-lib-report": "^1.1.2", - "istanbul-lib-source-maps": "^1.2.2", - "istanbul-reports": "^1.1.3", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" + "async": "2.5.0", + "fileset": "2.0.3", + "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.9.1", + "istanbul-lib-report": "1.1.2", + "istanbul-lib-source-maps": "1.2.2", + "istanbul-reports": "1.1.3", + "js-yaml": "3.10.0", + "mkdirp": "0.5.1", + "once": "1.4.0" }, "dependencies": { "async": { @@ -5082,7 +5082,7 @@ "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "dev": true, "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.4" } } } @@ -5099,7 +5099,7 @@ "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { - "append-transform": "^0.4.0" + "append-transform": "0.4.0" } }, "istanbul-lib-instrument": { @@ -5108,13 +5108,13 @@ "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.1.1", - "semver": "^5.3.0" + "babel-generator": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.4.1" } }, "istanbul-lib-report": { @@ -5123,10 +5123,10 @@ "integrity": "sha512-UTv4VGx+HZivJQwAo1wnRwe1KTvFpfi/NYwN7DcsrdzMXwpRT/Yb6r4SBPoHWj4VuQPakR32g4PUUeyKkdDkBA==", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.1.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" }, "dependencies": { "supports-color": { @@ -5135,7 +5135,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -5146,11 +5146,11 @@ "integrity": "sha512-8BfdqSfEdtip7/wo1RnrvLpHVEd8zMZEDmOFEnpC6dg0vXflHt9nvoAyQUzig2uMSXfF2OBEYBV3CVjIL9JvaQ==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.1.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" }, "dependencies": { "debug": { @@ -5168,12 +5168,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "rimraf": { @@ -5182,7 +5182,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } } } @@ -5193,7 +5193,7 @@ "integrity": "sha512-ZEelkHh8hrZNI5xDaKwPMFwDsUf5wIEI2bXAFGp1e6deR2mnEKBPhLJEgr4ZBt8Gi6Mj38E/C8kcy9XLggVO2Q==", "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.0.11" } }, "jade": { @@ -5230,10 +5230,10 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-8.4.2.tgz", "integrity": "sha1-vXd0ZY/pkFjYmU7R1LmWJITruFk=", "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "moment": "2.x.x", - "topo": "2.x.x" + "hoek": "4.2.0", + "isemail": "2.2.1", + "moment": "2.22.2", + "topo": "2.0.2" } }, "join-component": { @@ -5247,10 +5247,10 @@ "integrity": "sha512-6YX1g+lIl0/JDxjFFbgj7fz6i0bWFa2Hdc7PfGqFhynaEiYe1NJ3R1nda0VGaRiGU82OllR+EGDoWFpGr3k5Kg==", "dev": true, "requires": { - "config-chain": "~1.1.5", - "editorconfig": "^0.13.2", - "mkdirp": "~0.5.0", - "nopt": "~3.0.1" + "config-chain": "1.1.11", + "editorconfig": "0.13.3", + "mkdirp": "0.5.1", + "nopt": "3.0.6" } }, "js-cookie": { @@ -5275,8 +5275,8 @@ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.9", + "esprima": "4.0.0" } }, "jsbn": { @@ -5306,7 +5306,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stringify-safe": { @@ -5325,7 +5325,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.11" } }, "jsonify": { @@ -5345,15 +5345,15 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", "integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==", "requires": { - "jws": "^3.1.5", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1" + "jws": "3.1.5", + "lodash.includes": "4.3.0", + "lodash.isboolean": "3.0.3", + "lodash.isinteger": "4.0.4", + "lodash.isnumber": "3.0.3", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.once": "4.1.1", + "ms": "2.1.1" }, "dependencies": { "ms": { @@ -5381,7 +5381,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "jwks-rsa": { @@ -5390,11 +5390,11 @@ "integrity": "sha512-xg+fw7FOV4eGdDIEMqQJvPLmFv85h4uN+j/GKwJZAxlCrDQpM8ov1F709xKGEp/dG3l4TUxoSOeN6YK7+KpinQ==", "requires": { "@types/express-jwt": "0.0.34", - "debug": "^2.2.0", - "limiter": "^1.1.0", - "lru-memoizer": "^1.6.0", - "ms": "^2.0.0", - "request": "^2.73.0" + "debug": "2.6.9", + "limiter": "1.1.2", + "lru-memoizer": "1.11.1", + "ms": "2.0.0", + "request": "2.83.0" } }, "jws": { @@ -5402,8 +5402,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", "requires": { - "jwa": "^1.1.5", - "safe-buffer": "^5.0.1" + "jwa": "1.1.6", + "safe-buffer": "5.1.1" } }, "kind-of": { @@ -5412,7 +5412,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "latest-version": { @@ -5421,7 +5421,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-ass": { @@ -5443,7 +5443,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "le_node": { @@ -5477,8 +5477,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "libpq": { @@ -5487,7 +5487,7 @@ "integrity": "sha1-wt6xIeKPf4S9OyRRr/9otmY+dPk=", "requires": { "bindings": "1.2.1", - "nan": "^2.3.0" + "nan": "2.7.0" } }, "liftoff": { @@ -5496,15 +5496,15 @@ "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^0.4.2", - "fined": "^1.0.1", - "flagged-respawn": "^0.3.2", - "lodash.isplainobject": "^4.0.4", - "lodash.isstring": "^4.0.1", - "lodash.mapvalues": "^4.4.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "extend": "3.0.1", + "findup-sync": "0.4.3", + "fined": "1.1.0", + "flagged-respawn": "0.3.2", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.mapvalues": "4.6.0", + "rechoir": "0.6.2", + "resolve": "1.5.0" }, "dependencies": { "findup-sync": { @@ -5513,10 +5513,10 @@ "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", "dev": true, "requires": { - "detect-file": "^0.1.0", - "is-glob": "^2.0.1", - "micromatch": "^2.3.7", - "resolve-dir": "^0.1.0" + "detect-file": "0.1.0", + "is-glob": "2.0.1", + "micromatch": "2.3.11", + "resolve-dir": "0.1.1" } } } @@ -5532,10 +5532,10 @@ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" } }, "locate-path": { @@ -5544,8 +5544,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" }, "dependencies": { "path-exists": { @@ -5577,8 +5577,8 @@ "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" } }, "lodash._basecopy": { @@ -5611,9 +5611,9 @@ "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", "dev": true, "requires": { - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash.restparam": "^3.0.0" + "lodash._bindcallback": "3.0.1", + "lodash._isiterateecall": "3.0.9", + "lodash.restparam": "3.6.1" } }, "lodash._getnative": { @@ -5658,9 +5658,9 @@ "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", "dev": true, "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._createassigner": "^3.0.0", - "lodash.keys": "^3.0.0" + "lodash._baseassign": "3.2.0", + "lodash._createassigner": "3.1.1", + "lodash.keys": "3.1.2" } }, "lodash.cond": { @@ -5675,8 +5675,8 @@ "integrity": "sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw=", "dev": true, "requires": { - "lodash.assign": "^3.0.0", - "lodash.restparam": "^3.0.0" + "lodash.assign": "3.2.0", + "lodash.restparam": "3.6.1" } }, "lodash.escape": { @@ -5685,7 +5685,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.includes": { @@ -5736,9 +5736,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.mapvalues": { @@ -5764,15 +5764,15 @@ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -5781,8 +5781,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "lolex": { @@ -5803,7 +5803,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "lowercase-keys": { @@ -5823,10 +5823,10 @@ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-1.11.1.tgz", "integrity": "sha1-BpP2EAWTkUwC4ZK/m42TiEy/UNM=", "requires": { - "lock": "~0.1.2", - "lodash": "~4.5.1", - "lru-cache": "~4.0.0", - "very-fast-args": "^1.1.0" + "lock": "0.1.4", + "lodash": "4.5.1", + "lru-cache": "4.0.2", + "very-fast-args": "1.1.0" }, "dependencies": { "lodash": { @@ -5839,8 +5839,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } } } @@ -5851,7 +5851,7 @@ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "requires": { - "es5-ext": "~0.10.2" + "es5-ext": "0.10.35" } }, "make-dir": { @@ -5860,7 +5860,7 @@ "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -5894,7 +5894,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.1.0" } }, "memoizee": { @@ -5903,14 +5903,14 @@ "integrity": "sha1-vemBdmPJ5A/bKk6hw2cpYIeujI8=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.30", - "es6-weak-map": "^2.0.2", - "event-emitter": "^0.3.5", - "is-promise": "^2.1", - "lru-queue": "0.1", - "next-tick": "1", - "timers-ext": "^0.1.2" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-weak-map": "2.0.2", + "event-emitter": "0.3.5", + "is-promise": "2.1.0", + "lru-queue": "0.1.0", + "next-tick": "1.0.0", + "timers-ext": "0.1.2" } }, "memwatch-next": { @@ -5918,8 +5918,8 @@ "resolved": "https://registry.npmjs.org/memwatch-next/-/memwatch-next-0.3.0.tgz", "integrity": "sha1-IREFD5qQbgqi1ypOwPAInHhyb48=", "requires": { - "bindings": "^1.2.1", - "nan": "^2.3.2" + "bindings": "1.2.1", + "nan": "2.7.0" } }, "merge-descriptors": { @@ -5933,9 +5933,9 @@ "integrity": "sha1-49r41d7hDdLc59SuiNYrvud0drQ=", "requires": { "debug": "2.6.9", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" + "methods": "1.1.2", + "parseurl": "1.3.2", + "vary": "1.1.2" } }, "methods": { @@ -5949,19 +5949,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "millisecond": { @@ -5984,7 +5984,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "~1.30.0" + "mime-db": "1.30.0" } }, "mimic-fn": { @@ -5998,7 +5998,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.8" } }, "minimist": { @@ -6059,8 +6059,8 @@ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", "dev": true, "requires": { - "inherits": "2", - "minimatch": "0.3" + "inherits": "2.0.3", + "minimatch": "0.3.0" } }, "minimatch": { @@ -6069,8 +6069,8 @@ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", "dev": true, "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "lru-cache": "2.7.3", + "sigmund": "1.0.1" } }, "ms": { @@ -6097,7 +6097,7 @@ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz", "integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=", "requires": { - "moment": ">= 2.9.0" + "moment": "2.22.2" } }, "ms": { @@ -6126,9 +6126,9 @@ "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", "optional": true, "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" } }, "nan": { @@ -6171,16 +6171,16 @@ "integrity": "sha1-mWpW3EnZ8Wu/G3ik3gjxNjSzh40=", "dev": true, "requires": { - "chokidar": "^1.7.0", - "debug": "^2.6.8", - "es6-promise": "^3.3.1", - "ignore-by-default": "^1.0.1", - "lodash.defaults": "^3.1.2", - "minimatch": "^3.0.4", - "ps-tree": "^1.1.0", - "touch": "^3.1.0", + "chokidar": "1.7.0", + "debug": "2.6.9", + "es6-promise": "3.3.1", + "ignore-by-default": "1.0.1", + "lodash.defaults": "3.1.2", + "minimatch": "3.0.4", + "ps-tree": "1.1.0", + "touch": "3.1.0", "undefsafe": "0.0.3", - "update-notifier": "^2.2.0" + "update-notifier": "2.3.0" } }, "nopt": { @@ -6189,7 +6189,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } }, "normalize-package-data": { @@ -6198,10 +6198,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" } }, "normalize-path": { @@ -6210,7 +6210,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "npm-run-path": { @@ -6219,7 +6219,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -6244,10 +6244,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "array-each": "1.0.1", + "array-slice": "1.0.0", + "for-own": "1.0.0", + "isobject": "3.0.1" }, "dependencies": { "for-own": { @@ -6256,7 +6256,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "isobject": { @@ -6273,8 +6273,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "object.pick": { @@ -6283,7 +6283,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -6307,7 +6307,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -6322,8 +6322,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" }, "dependencies": { "wordwrap": { @@ -6340,12 +6340,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "orchestrator": { @@ -6354,9 +6354,9 @@ "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "dev": true, "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.0" } }, "ordered-read-streams": { @@ -6376,9 +6376,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -6393,9 +6393,9 @@ "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "dev": true, "requires": { - "graceful-fs": "^4.1.4", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.0" + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "object-assign": "4.1.1" } }, "p-finally": { @@ -6416,7 +6416,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.1.0" } }, "package-json": { @@ -6425,10 +6425,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.1", + "registry-url": "3.1.0", + "semver": "5.4.1" } }, "packet-reader": { @@ -6442,9 +6442,9 @@ "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", "dev": true, "requires": { - "is-absolute": "^0.2.3", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "0.2.6", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-glob": { @@ -6453,10 +6453,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -6465,7 +6465,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "parse-passwd": { @@ -6485,7 +6485,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -6517,7 +6517,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -6537,7 +6537,7 @@ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "2.3.0" } }, "pause-stream": { @@ -6546,7 +6546,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "performance-now": { @@ -6564,9 +6564,9 @@ "js-string-escape": "1.0.1", "packet-reader": "0.2.0", "pg-connection-string": "0.1.3", - "pg-types": "1.*", + "pg-types": "1.12.1", "pgpass": "0.0.3", - "semver": "^4.1.0" + "semver": "4.3.6" }, "dependencies": { "semver": { @@ -6586,7 +6586,7 @@ "resolved": "https://registry.npmjs.org/pg-native/-/pg-native-1.10.1.tgz", "integrity": "sha1-lOYcy7hafzQ2suUmMVx1gRB/5Aw=", "requires": { - "libpq": "^1.7.0", + "libpq": "1.8.7", "pg-types": "1.6.0", "readable-stream": "1.0.31" }, @@ -6601,10 +6601,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz", "integrity": "sha1-jyUC4LyeOw2huUUgqrtOJgPsr64=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } } } @@ -6614,10 +6614,10 @@ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", "requires": { - "postgres-array": "~1.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.0", - "postgres-interval": "^1.1.0" + "postgres-array": "1.0.2", + "postgres-bytea": "1.0.0", + "postgres-date": "1.0.3", + "postgres-interval": "1.1.1" } }, "pgpass": { @@ -6625,7 +6625,7 @@ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-0.0.3.tgz", "integrity": "sha1-EuZ+NDsxicLzEgbrycwL7//PkUA=", "requires": { - "split": "~0.3" + "split": "0.3.3" } }, "pify": { @@ -6646,7 +6646,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -6655,7 +6655,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } }, "pluralize": { @@ -6684,7 +6684,7 @@ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.1.tgz", "integrity": "sha512-OkuCi9t/3CZmeQreutGgx/OVNv9MKHGIT5jH8KldQ4NLYXkvmT9nDVxEuCENlNwhlGPE374oA/xMqn05G49pHA==", "requires": { - "xtend": "^4.0.0" + "xtend": "4.0.1" } }, "precond": { @@ -6738,7 +6738,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "proto-list": { @@ -6752,7 +6752,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.1.2", "ipaddr.js": "1.5.2" } }, @@ -6762,7 +6762,7 @@ "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "dev": true, "requires": { - "event-stream": "~3.3.0" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -6791,8 +6791,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -6801,7 +6801,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6810,7 +6810,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6821,7 +6821,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6848,10 +6848,10 @@ "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", "dev": true, "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -6868,9 +6868,9 @@ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" } }, "read-pkg-up": { @@ -6879,8 +6879,8 @@ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "find-up": "2.1.0", + "read-pkg": "2.0.0" }, "dependencies": { "find-up": { @@ -6889,7 +6889,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } } } @@ -6899,10 +6899,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "readdirp": { @@ -6911,10 +6911,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" }, "dependencies": { "isarray": { @@ -6929,13 +6929,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -6944,7 +6944,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -6955,8 +6955,8 @@ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", "mute-stream": "0.0.5" } }, @@ -6976,7 +6976,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.5.0" } }, "reconnect-core": { @@ -6984,7 +6984,7 @@ "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", "requires": { - "backoff": "~2.5.0" + "backoff": "2.5.0" } }, "redefine": { @@ -7011,9 +7011,9 @@ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "private": "0.1.8" }, "dependencies": { "babel-runtime": { @@ -7022,8 +7022,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } } } @@ -7034,7 +7034,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regexpu-core": { @@ -7043,9 +7043,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "registry-auth-token": { @@ -7054,8 +7054,8 @@ "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.2", + "safe-buffer": "5.1.1" } }, "registry-url": { @@ -7064,7 +7064,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.2" } }, "regjsgen": { @@ -7079,7 +7079,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -7119,7 +7119,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "replace-ext": { @@ -7133,28 +7133,28 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" } }, "require-directory": { @@ -7175,8 +7175,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" } }, "resolve": { @@ -7185,7 +7185,7 @@ "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "resolve-dir": { @@ -7194,8 +7194,8 @@ "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", "dev": true, "requires": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" + "expand-tilde": "1.2.2", + "global-modules": "0.2.3" } }, "resolve-from": { @@ -7210,8 +7210,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" + "exit-hook": "1.1.1", + "onetime": "1.1.0" } }, "retry-as-promised": { @@ -7219,8 +7219,8 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", "requires": { - "bluebird": "^3.4.6", - "debug": "^2.6.9" + "bluebird": "3.5.1", + "debug": "2.6.9" } }, "right-align": { @@ -7230,7 +7230,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -7238,7 +7238,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "requires": { - "glob": "^6.0.1" + "glob": "6.0.4" } }, "run-async": { @@ -7247,7 +7247,7 @@ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true, "requires": { - "once": "^1.3.0" + "once": "1.4.0" } }, "rx-lite": { @@ -7294,7 +7294,7 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "5.4.1" } }, "send": { @@ -7303,18 +7303,18 @@ "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", "requires": { "debug": "2.6.9", - "depd": "~1.1.1", - "destroy": "~1.0.4", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.3.1" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" }, "dependencies": { "statuses": { @@ -7329,21 +7329,21 @@ "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", "integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", "requires": { - "bluebird": "^3.3.4", - "depd": "^1.1.0", - "dottie": "^1.0.0", + "bluebird": "3.5.1", + "depd": "1.1.1", + "dottie": "1.1.1", "generic-pool": "2.4.2", - "inflection": "^1.6.0", + "inflection": "1.12.0", "lodash": "4.12.0", - "moment": "^2.13.0", - "moment-timezone": "^0.5.4", - "retry-as-promised": "^2.0.0", - "semver": "^5.0.1", + "moment": "2.22.2", + "moment-timezone": "0.5.14", + "retry-as-promised": "2.3.2", + "semver": "5.4.1", "shimmer": "1.1.0", - "terraformer-wkt-parser": "^1.1.0", - "toposort-class": "^1.0.1", - "uuid": "^3.0.0", - "validator": "^5.2.0", + "terraformer-wkt-parser": "1.1.2", + "toposort-class": "1.0.1", + "uuid": "3.1.0", + "validator": "5.7.0", "wkx": "0.2.0" }, "dependencies": { @@ -7360,18 +7360,18 @@ "integrity": "sha1-QwTM5g5JkWlgP4ON7bq0IcmEnnQ=", "dev": true, "requires": { - "bluebird": "^3.5.0", - "cli-color": "~1.2.0", - "findup-sync": "^1.0.0", - "fs-extra": "^4.0.1", - "gulp": "^3.9.1", - "gulp-help": "~1.6.1", - "js-beautify": "^1.6.11", - "lodash": "^4.17.4", - "moment": "^2.17.1", - "resolve": "^1.3.3", - "umzug": "^1.12.0", - "yargs": "^8.0.1" + "bluebird": "3.5.1", + "cli-color": "1.2.0", + "findup-sync": "1.0.0", + "fs-extra": "4.0.2", + "gulp": "3.9.1", + "gulp-help": "1.6.1", + "js-beautify": "1.7.4", + "lodash": "4.17.4", + "moment": "2.22.2", + "resolve": "1.5.0", + "umzug": "1.12.0", + "yargs": "8.0.2" }, "dependencies": { "ansi-regex": { @@ -7392,9 +7392,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" }, "dependencies": { "string-width": { @@ -7403,9 +7403,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -7416,8 +7416,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -7432,7 +7432,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -7443,19 +7443,19 @@ "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", "dev": true, "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" } } } @@ -7471,9 +7471,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", "requires": { - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.1" } }, @@ -7500,7 +7500,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -7515,9 +7515,9 @@ "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.2", + "interpret": "1.0.4", + "rechoir": "0.6.2" }, "dependencies": { "glob": { @@ -7526,12 +7526,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } @@ -7562,7 +7562,7 @@ "formatio": "1.1.1", "lolex": "1.3.2", "samsam": "1.1.2", - "util": ">=0.10.3 <1" + "util": "0.10.3" } }, "sinon-chai": { @@ -7583,7 +7583,7 @@ "integrity": "sha1-h4+h1E0I7rDyb7IBjvhinrGjq5Q=", "dev": true, "requires": { - "nan": ">=2.5.1" + "nan": "2.7.0" } }, "slice-ansi": { @@ -7597,7 +7597,7 @@ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.0" } }, "source-map": { @@ -7612,7 +7612,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.7" } }, "sparkles": { @@ -7627,7 +7627,7 @@ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "dev": true, "requires": { - "spdx-license-ids": "^1.0.2" + "spdx-license-ids": "1.2.2" } }, "spdx-expression-parse": { @@ -7647,7 +7647,7 @@ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "requires": { - "through": "2" + "through": "2.3.8" } }, "sprintf-js": { @@ -7661,14 +7661,14 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" } }, "statuses": { @@ -7682,7 +7682,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1" } }, "stream-consume": { @@ -7690,22 +7690,22 @@ "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=" }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -7716,7 +7716,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -7742,16 +7742,16 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.0.tgz", "integrity": "sha512-71XGWgtn70TNwgmgYa69dPOYg55aU9FCahjUNY03rOrKvaTCaU3b9MeZmqonmf9Od96SCxr3vGfEAnhM7dtxCw==", "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.1.1", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.0.5" + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.1", + "formidable": "1.1.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.3" }, "dependencies": { "debug": { @@ -7772,13 +7772,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -7786,7 +7786,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -7802,8 +7802,8 @@ "integrity": "sha1-oFgIHXiPFRXUcA11Aogea3WeRM0=", "dev": true, "requires": { - "methods": "1.x", - "superagent": "^2.0.0" + "methods": "1.1.2", + "superagent": "2.3.0" }, "dependencies": { "form-data": { @@ -7812,9 +7812,9 @@ "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", "dev": true, "requires": { - "async": "^1.5.2", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.10" + "async": "1.5.2", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" } }, "isarray": { @@ -7829,13 +7829,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -7844,7 +7844,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "superagent": { @@ -7853,16 +7853,16 @@ "integrity": "sha1-cDUpoHFOV+EjlZ3e+84ZOy5Q0RU=", "dev": true, "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.0.6", - "debug": "^2.2.0", - "extend": "^3.0.0", + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "2.6.9", + "extend": "3.0.1", "form-data": "1.0.0-rc4", - "formidable": "^1.0.17", - "methods": "^1.1.1", - "mime": "^1.3.4", - "qs": "^6.1.0", - "readable-stream": "^2.0.5" + "formidable": "1.1.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.3" } } } @@ -7878,12 +7878,12 @@ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.4", "slice-ansi": "0.0.4", - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -7904,8 +7904,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -7914,24 +7914,23 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } }, "tc-core-library-js": { "version": "github:appirio-tech/tc-core-library-js#df1f5c1a5578d3d1e475bfb4a7413d9dec25525a", - "from": "github:appirio-tech/tc-core-library-js#v2.3", - "requires": { - "auth0-js": "^9.4.2", - "axios": "^0.12.0", - "bunyan": "^1.8.1", - "config": "^1.21.0", - "jsonwebtoken": "^7.0.0", - "jwks-rsa": "^1.2.1", - "le_node": "^1.3.1", - "lodash": "^4.13.1", - "millisecond": "^0.1.2" + "requires": { + "auth0-js": "9.6.0", + "axios": "0.12.0", + "bunyan": "1.8.12", + "config": "1.27.0", + "jsonwebtoken": "7.4.3", + "jwks-rsa": "1.2.1", + "le_node": "1.7.1", + "lodash": "4.17.4", + "millisecond": "0.1.2" }, "dependencies": { "axios": { @@ -7947,8 +7946,8 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=", "requires": { - "debug": "^2.2.0", - "stream-consume": "^0.1.0" + "debug": "2.6.9", + "stream-consume": "0.1.0" } }, "hoek": { @@ -7966,10 +7965,10 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", "requires": { - "hoek": "2.x.x", - "isemail": "1.x.x", - "moment": "2.x.x", - "topo": "1.x.x" + "hoek": "2.16.3", + "isemail": "1.2.0", + "moment": "2.22.2", + "topo": "1.1.0" } }, "jsonwebtoken": { @@ -7977,11 +7976,11 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", "requires": { - "joi": "^6.10.1", - "jws": "^3.1.4", - "lodash.once": "^4.0.0", - "ms": "^2.0.0", - "xtend": "^4.0.1" + "joi": "6.10.1", + "jws": "3.1.5", + "lodash.once": "4.1.1", + "ms": "2.0.0", + "xtend": "4.0.1" } }, "topo": { @@ -7989,7 +7988,7 @@ "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } } } @@ -8000,7 +7999,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "terraformer": { @@ -8008,7 +8007,7 @@ "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.8.tgz", "integrity": "sha1-UeCtiXRvzyFh3G9lqnDkI3fItZM=", "requires": { - "@types/geojson": "^1.0.0" + "@types/geojson": "1.0.6" } }, "terraformer-wkt-parser": { @@ -8016,7 +8015,7 @@ "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz", "integrity": "sha1-M2oMj8gglKWv+DKI9prt7NNpvww=", "requires": { - "terraformer": "~1.0.5" + "terraformer": "1.0.8" } }, "text-table": { @@ -8036,8 +8035,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.3", + "xtend": "4.0.1" }, "dependencies": { "isarray": { @@ -8052,13 +8051,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -8067,7 +8066,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -8078,7 +8077,7 @@ "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "time-stamp": { @@ -8099,8 +8098,8 @@ "integrity": "sha1-YcxHp2wavTGV8UUn+XjViulMUgQ=", "dev": true, "requires": { - "es5-ext": "~0.10.14", - "next-tick": "1" + "es5-ext": "0.10.35", + "next-tick": "1.0.0" } }, "to-fast-properties": { @@ -8120,7 +8119,7 @@ "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.0" } }, "toposort-class": { @@ -8134,7 +8133,7 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "~1.0.10" + "nopt": "1.0.10" }, "dependencies": { "nopt": { @@ -8143,7 +8142,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } } } @@ -8153,7 +8152,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" }, "dependencies": { "punycode": { @@ -8185,7 +8184,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "tweetnacl": { @@ -8200,7 +8199,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-detect": { @@ -8215,7 +8214,7 @@ "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.15" + "mime-types": "2.1.17" } }, "typedarray": { @@ -8231,9 +8230,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -8249,11 +8248,11 @@ "integrity": "sha1-p5yR8oYu7jEwxsNH8rkK1opm6Lg=", "dev": true, "requires": { - "bluebird": "^3.4.1", - "lodash": "^4.17.0", - "moment": "^2.16.0", - "redefine": "^0.2.0", - "resolve": "^1.0.0" + "bluebird": "3.5.1", + "lodash": "4.17.4", + "moment": "2.22.2", + "redefine": "0.2.1", + "resolve": "1.5.0" } }, "unc-path-regex": { @@ -8280,7 +8279,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "universalify": { @@ -8306,15 +8305,15 @@ "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.2.2", + "chalk": "2.3.0", + "configstore": "3.1.1", + "import-lazy": "2.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" }, "dependencies": { "ansi-styles": { @@ -8323,7 +8322,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { @@ -8332,9 +8331,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" } }, "has-flag": { @@ -8349,7 +8348,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -8374,7 +8373,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "urlencode": { @@ -8382,7 +8381,7 @@ "resolved": "https://registry.npmjs.org/urlencode/-/urlencode-1.1.0.tgz", "integrity": "sha1-HyuibwE8hfATP3o61v8nMK33y7c=", "requires": { - "iconv-lite": "~0.4.11" + "iconv-lite": "0.4.19" } }, "user-home": { @@ -8429,7 +8428,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" } }, "validate-npm-package-license": { @@ -8438,8 +8437,8 @@ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true, "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" } }, "validator": { @@ -8457,9 +8456,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "very-fast-args": { @@ -8473,8 +8472,8 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.2", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" }, "dependencies": { @@ -8492,14 +8491,14 @@ "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", "dev": true, "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -8514,7 +8513,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "^1.1.0" + "natives": "1.1.0" } }, "readable-stream": { @@ -8523,10 +8522,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "strip-bom": { @@ -8535,8 +8534,8 @@ "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", "dev": true, "requires": { - "first-chunk-stream": "^1.0.0", - "is-utf8": "^0.2.0" + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" } }, "through2": { @@ -8545,8 +8544,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -8555,8 +8554,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -8567,7 +8566,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -8582,7 +8581,7 @@ "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", "dev": true, "requires": { - "string-width": "^1.0.1" + "string-width": "1.0.2" } }, "winchan": { @@ -8614,8 +8613,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" } }, "wrappy": { @@ -8629,7 +8628,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "write-file-atomic": { @@ -8638,9 +8637,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "xdg-basedir": { @@ -8654,8 +8653,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "^4.1.0" + "sax": "1.2.1", + "xmlbuilder": "4.2.1" } }, "xmlbuilder": { @@ -8663,7 +8662,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "requires": { - "lodash": "^4.0.0" + "lodash": "4.17.4" } }, "xtend": { @@ -8689,9 +8688,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } }, @@ -8701,7 +8700,7 @@ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { From f3795bab819cf71dfc15bde5d579b2cc747c040c Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 14:17:07 +0530 Subject: [PATCH 24/77] Fixed issue with missing events for cascaded milestone updates --- src/events/busApi.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 6d9d2f26..488f27a5 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -498,9 +498,11 @@ module.exports = (app, logger) => { sendMilestoneNotification(req, original, updated, project); logger.debug('cascadedUpdates', cascadedUpdates); - _.each(cascadedUpdates, cascadedUpdate => - sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project), - ); + if (cascadedUpdates && cascadedUpdates.milestones && cascadedUpdates.milestones.length > 0) { + _.each(cascadedUpdates.milestones, cascadedUpdate => + sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project), + ); + } }) .catch(err => null); // eslint-disable-line no-unused-vars }); From 228799fb1e73a0874725332d1ade8f0ed9247541 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 14:51:34 +0530 Subject: [PATCH 25/77] more logging statements --- src/events/busApi.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/events/busApi.js b/src/events/busApi.js index 488f27a5..93027861 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -425,6 +425,7 @@ module.exports = (app, logger) => { * @returns {Promise} void */ function sendMilestoneNotification(req, original, updated, project) { + logger.debug('sendMilestoneNotification', original, updated); // Send transition events if (original.status !== updated.status) { let event; From ae5bf7155e812ca8b5c64b50ffbb4132d5ca1d83 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 15:17:45 +0530 Subject: [PATCH 26/77] one more update to fix the milestone active event --- src/routes/milestones/update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index cbb2f2a3..cd93d06d 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -76,8 +76,8 @@ function updateComingMilestones(origMilestone, updMilestone) { // Resolve promise with all original and updated milestones return Promise.all(promises).then(updatedMilestones => ({ - originalMilestones: affectedMilestones, - updatedMilestones, + originalMilestones: affectedMilestones.map(am => _.omit(am.toJSON(), 'deletedAt', 'deletedBy')), + updatedMilestones: updatedMilestones.map(um => _.omot(um.toJSON(), 'deletedAt', 'deletedBy')), })); }); } From a86fbbadfb8d3393d378d06e7d2a9b14887faa12 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 15:43:46 +0530 Subject: [PATCH 27/77] copying metadata from milestone template --- src/routes/timelines/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/timelines/create.js b/src/routes/timelines/create.js index f7a5e3fc..44ec10be 100644 --- a/src/routes/timelines/create.js +++ b/src/routes/timelines/create.js @@ -85,7 +85,7 @@ module.exports = [ blockedText: mt.blockedText, completedText: mt.completedText, hidden: !!mt.hidden, - details: {}, + details: { metadata: mt.metadata }, status: MILESTONE_STATUS.REVIEWED, startDate: startDate.format(), endDate: endDate.format(), From ffdfac63b24edb5de106e04caab6c4018a3b8aab Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 15:45:09 +0530 Subject: [PATCH 28/77] fixed typo --- src/routes/milestones/update.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index cd93d06d..fcbe5f64 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -77,7 +77,7 @@ function updateComingMilestones(origMilestone, updMilestone) { // Resolve promise with all original and updated milestones return Promise.all(promises).then(updatedMilestones => ({ originalMilestones: affectedMilestones.map(am => _.omit(am.toJSON(), 'deletedAt', 'deletedBy')), - updatedMilestones: updatedMilestones.map(um => _.omot(um.toJSON(), 'deletedAt', 'deletedBy')), + updatedMilestones: updatedMilestones.map(um => _.omit(um.toJSON(), 'deletedAt', 'deletedBy')), })); }); } From 67450545eb682e0d61f6cb53cb807ec04086c435 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 16:58:55 +0530 Subject: [PATCH 29/77] one more update to fix the milestone active event --- src/routes/milestones/update.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index fcbe5f64..58da00a8 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -33,12 +33,14 @@ function updateComingMilestones(origMilestone, updMilestone) { let updMilestoneEndDate = moment.utc(updMSStartDate).add(updMilestone.duration - 1, 'days').toDate(); // if the milestone, in context, is completed, overrides the end date to the completion date updMilestoneEndDate = updMilestone.completionDate ? updMilestone.completionDate : updMilestoneEndDate; + let originalMilestones; return models.Milestone.findAll({ where: { timelineId: updMilestone.timelineId, order: { $gt: updMilestone.order }, }, }).then((affectedMilestones) => { + originalMilestones = affectedMilestones.map(am => _.omit(am.toJSON(), 'deletedAt', 'deletedBy')); const comingMilestones = _.sortBy(affectedMilestones, 'order'); // calculates the schedule start date for the next milestone let startDate = moment.utc(updMilestoneEndDate).add(1, 'days').toDate(); @@ -76,7 +78,7 @@ function updateComingMilestones(origMilestone, updMilestone) { // Resolve promise with all original and updated milestones return Promise.all(promises).then(updatedMilestones => ({ - originalMilestones: affectedMilestones.map(am => _.omit(am.toJSON(), 'deletedAt', 'deletedBy')), + originalMilestones, updatedMilestones: updatedMilestones.map(um => _.omit(um.toJSON(), 'deletedAt', 'deletedBy')), })); }); From a1a3be64b63239aba39078210a1821b86cc1d03a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 3 Sep 2018 17:39:01 +0530 Subject: [PATCH 30/77] Revert "Revert utils.js#getTopcoderUser()" This reverts commit cddec286cabd475f836abdaf2cfbc689678fc52a. --- src/util.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/util.js b/src/util.js index bc1fe325..34e12c7d 100644 --- a/src/util.js +++ b/src/util.js @@ -307,14 +307,19 @@ _.assignIn(util, { httpClient.defaults.headers.common.Accept = 'application/json'; httpClient.defaults.headers.common['Content-Type'] = 'application/json'; httpClient.defaults.headers.common.Authorization = `Bearer ${jwtToken}`; - return httpClient.get(`${config.identityServiceEndpoint}users/${userId}`).then((response) => { - if (response.data && response.data.result + return httpClient.get(`${config.identityServiceEndpoint}users`, { + params: { + filter: `id=${userId}`, + }, + }) + .then((response) => { + if (response.data && response.data.result && response.data.result.status === 200 && response.data.result.content - && response.data.result.content.length > 0) { - return response.data.result.content[0]; - } - return null; - }); + && response.data.result.content.length === 1) { + return response.data.result.content[0]; + } + return null; + }); }, /** From 186ba22e6bf6218f9996f57dad078c1c14628515 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 4 Sep 2018 12:59:03 +0530 Subject: [PATCH 31/77] Added timeline modified event --- src/constants.js | 4 ++++ src/events/busApi.js | 18 ++++++++++++++++++ src/routes/milestones/update.js | 8 ++++++++ 3 files changed, 30 insertions(+) diff --git a/src/constants.js b/src/constants.js index 90248b1f..99ca4e5d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -96,6 +96,10 @@ export const BUS_API_EVENT = { PROJECT_PLAN_READY: 'notifications.connect.project.planReady', + // When milestone is added/deleted to/from the phase, + // When milestone is updated for duration/startDate/endDate/status + TIMELINE_MODIFIED: 'notifications.connect.project.phase.timelineModified', + // When specification of a product is modified PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'notifications.connect.project.productSpecificationModified', diff --git a/src/events/busApi.js b/src/events/busApi.js index 93027861..2c8481f6 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -504,6 +504,24 @@ module.exports = (app, logger) => { sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project), ); } + + // if timeline is modified + if (cascadedUpdates && cascadedUpdates.timeline) { + const timeline = cascadedUpdates.timeline; + // if endDate of the timeline is modified, raise TIMELINE_MODIFIED event + if (timeline.original.endDate !== timeline.updated.endDate) { + // Raise Timeline changed event + createEvent(BUS_API_EVENT.TIMELINE_MODIFIED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + original: timeline.original, + updated: timeline.updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + } }) .catch(err => null); // eslint-disable-line no-unused-vars }); diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index 58da00a8..90e179d5 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -135,6 +135,7 @@ module.exports = [ }); const timeline = req.timeline; + const originalTimeline = _.omit(timeline.toJSON(), 'deletedAt', 'deletedBy'); let original; let updated; @@ -275,6 +276,13 @@ module.exports = [ 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, From 07154fc7a4c2d08e9c9f602b75fc62babecdc11c Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Fri, 7 Sep 2018 16:48:30 +0800 Subject: [PATCH 32/77] Some improvements for local deployment to work more out of the box. --- config/sample.local.js | 5 ++-- local/mock-services/services.json | 50 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/config/sample.local.js b/config/sample.local.js index 7a1b2a69..bec4dd70 100644 --- a/config/sample.local.js +++ b/config/sample.local.js @@ -5,6 +5,7 @@ if (process.env.NODE_ENV === 'test') { config = require('./test.json'); } else { config = { + identityServiceEndpoint: "http://dockerhost:3001/", authSecret: 'secret', authDomain: 'topcoder-dev.com', logLevel: 'debug', @@ -14,9 +15,9 @@ if (process.env.NODE_ENV === 'test') { fileServiceEndpoint: 'https://api.topcoder-dev.com/v3/files/', directProjectServiceEndpoint: 'https://api.topcoder-dev.com/v3/direct', connectProjectsUrl: 'https://connect.topcoder-dev.com/projects/', - memberServiceEndpoint: 'http://dockerhost:3001/members', + memberServiceEndpoint: 'http://dockerhost:3001/v3/members', dbConfig: { - masterUrl: 'postgres://coder:mysecretpassword@dockerhost:54321/projectsdb', + masterUrl: 'postgres://coder:mysecretpassword@dockerhost:5432/projectsdb', maxPoolSize: 50, minPoolSize: 4, idleTimeout: 1000, diff --git a/local/mock-services/services.json b/local/mock-services/services.json index 87ef7863..d77800cb 100644 --- a/local/mock-services/services.json +++ b/local/mock-services/services.json @@ -249,6 +249,56 @@ } }, "version": "v3" + }, + { + "id": "test_admin1", + "result": { + "success": true, + "status": 200, + "metadata": null, + "content": { + "maxRating": { + "rating": 1114, + "track": "DATA_SCIENCE", + "subTrack": "SRM" + }, + "createdBy": "40011578", + "updatedBy": "40011578", + "userId": 40135978, + "firstName": "Adminname", + "lastName": "Adminlastname", + "quote": "It is a mistake to think you can solve any major problems just with potatoes.", + "description": null, + "otherLangName": null, + "handle": "test_admin1", + "handleLower": "test_admin1", + "status": "ACTIVE", + "email": "pshah1@test.com", + "addresses": [ + { + "streetAddr1": "100 Main Street", + "streetAddr2": "", + "city": "Chicago", + "zip": "60601", + "stateCode": "IL", + "type": "HOME", + "updatedAt": null, + "createdAt": null, + "createdBy": null, + "updatedBy": null + } + ], + "homeCountryCode": "USA", + "competitionCountryCode": "USA", + "photoURL": null, + "tracks": [ + "DEVELOP" + ], + "updatedAt": "2015-12-02T14:00Z", + "createdAt": "2014-04-10T10:55Z" + } + }, + "version": "v3" } ] } From 68a9c03722cfc4015741c8e7f31a0f8ec24ca2b0 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 7 Sep 2018 15:18:26 +0530 Subject: [PATCH 33/77] Raised phase progress event when milestone completed --- src/events/busApi.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 2c8481f6..27f1b6a0 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -428,25 +428,28 @@ module.exports = (app, logger) => { logger.debug('sendMilestoneNotification', original, updated); // Send transition events if (original.status !== updated.status) { - let event; + let events; if (updated.status === MILESTONE_STATUS.COMPLETED) { - event = BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED; + // on milestone completion, raise milestone completion and project phase progress events + events = [BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED, BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS]; } else if (updated.status === MILESTONE_STATUS.ACTIVE) { - event = BUS_API_EVENT.MILESTONE_TRANSITION_ACTIVE; + events = [BUS_API_EVENT.MILESTONE_TRANSITION_ACTIVE]; } - if (event) { - createEvent(event, { - projectId: project.id, - projectName: project.name, - projectUrl: connectProjectUrl(project.id), - timelineId: req.timeline.id, - timelineName: req.timeline.name, - originalMilestone: original, - updatedMilestone: updated, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); + if (events) { + events.forEach(event => { + createEvent(event, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + timelineId: req.timeline.id, + timelineName: req.timeline.name, + originalMilestone: original, + updatedMilestone: updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + }); } } From aa1e41032d0683fbafef5b3fd3b6074481b71782 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 7 Sep 2018 15:18:49 +0530 Subject: [PATCH 34/77] possible fix for spurious project progress update event --- src/routes/phases/update.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/phases/update.js b/src/routes/phases/update.js index d2c6a9b4..56cb287c 100644 --- a/src/routes/phases/update.js +++ b/src/routes/phases/update.js @@ -158,7 +158,7 @@ module.exports = [ { correlationId: req.id }, ); req.app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, - { req, original: previousValue, updated }); + { req, original: previousValue, updated: _.clone(updated.get({ plain: true })) }); res.json(util.wrapResponse(req.id, updated)); }) From 5f65706221693a53b4abb3985a98c8105862cf73 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 7 Sep 2018 15:20:11 +0530 Subject: [PATCH 35/77] lint fix --- src/events/busApi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index 27f1b6a0..aed5a8ee 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -437,7 +437,7 @@ module.exports = (app, logger) => { } if (events) { - events.forEach(event => { + events.forEach((event) => { createEvent(event, { projectId: project.id, projectName: project.name, From 247e20afd9b8e0634d8dd7b4a74eb2ba141d77e3 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 10 Sep 2018 11:29:57 +0530 Subject: [PATCH 36/77] reverted the raise of phase event from milestone event. Milestone events should be consumed by project service and raise the phase updated event instead. --- src/events/busApi.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index aed5a8ee..2c8481f6 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -428,28 +428,25 @@ module.exports = (app, logger) => { logger.debug('sendMilestoneNotification', original, updated); // Send transition events if (original.status !== updated.status) { - let events; + let event; if (updated.status === MILESTONE_STATUS.COMPLETED) { - // on milestone completion, raise milestone completion and project phase progress events - events = [BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED, BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS]; + event = BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED; } else if (updated.status === MILESTONE_STATUS.ACTIVE) { - events = [BUS_API_EVENT.MILESTONE_TRANSITION_ACTIVE]; + event = BUS_API_EVENT.MILESTONE_TRANSITION_ACTIVE; } - if (events) { - events.forEach((event) => { - createEvent(event, { - projectId: project.id, - projectName: project.name, - projectUrl: connectProjectUrl(project.id), - timelineId: req.timeline.id, - timelineName: req.timeline.name, - originalMilestone: original, - updatedMilestone: updated, - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - }); + if (event) { + createEvent(event, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + timelineId: req.timeline.id, + timelineName: req.timeline.name, + originalMilestone: original, + updatedMilestone: updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); } } From ca31eb0e5301a1b2f441b31dfc3b8ae6c9bf8b69 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 12 Sep 2018 15:20:21 +0530 Subject: [PATCH 37/77] Added details of original and updated phases with bus event payload --- src/events/busApi.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/events/busApi.js b/src/events/busApi.js index 2c8481f6..c2756b5b 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -320,6 +320,8 @@ module.exports = (app, logger) => { events.forEach(event => createEvent(event, { projectId, phaseId, + originalPhase: original, + updatedPhase: updated, projectName: project.name, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, From fc183e08af1e49cc655278f0fbf1e98575759a1e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 12 Sep 2018 16:59:30 +0530 Subject: [PATCH 38/77] Made `indexName` as required field for delete index endpoint --- src/routes/admin/project-delete-index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/routes/admin/project-delete-index.js b/src/routes/admin/project-delete-index.js index 472a1322..f94b9e81 100644 --- a/src/routes/admin/project-delete-index.js +++ b/src/routes/admin/project-delete-index.js @@ -29,10 +29,13 @@ module.exports = [ (req, res, next) => { // eslint-disable-line no-unused-vars const logger = req.log; logger.debug('Entered Admin#deleteIndex'); - const indexName = _.get(req, 'body.param.indexName', ES_PROJECT_INDEX); - // const docType = _.get(req, 'body.param.docType', ES_PROJECT_TYPE); + const indexName = _.get(req, 'body.param.indexName'); logger.debug('indexName', indexName); - // logger.debug('docType', docType); + if (!indexName) { + const apiErr = new Error('indexName is required'); + apiErr.status = 400; + return Promise.reject(apiErr); + } const esClient = util.getElasticSearchClient(); esClient.indices.delete({ From ff813158118a5fe829baf100955518b51bf7f976 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 12 Sep 2018 17:03:54 +0530 Subject: [PATCH 39/77] lint fix --- src/routes/admin/project-delete-index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/routes/admin/project-delete-index.js b/src/routes/admin/project-delete-index.js index f94b9e81..eb72ccc6 100644 --- a/src/routes/admin/project-delete-index.js +++ b/src/routes/admin/project-delete-index.js @@ -2,7 +2,6 @@ /* globals Promise */ import _ from 'lodash'; -import config from 'config'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import util from '../../util'; @@ -17,8 +16,6 @@ import util from '../../util'; // var permissions = require('tc-core-library-js').middleware.permissions const permissions = tcMiddleware.permissions; -const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); -// const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); module.exports = [ permissions('project.admin'), @@ -43,6 +40,6 @@ module.exports = [ // we would want to ignore no such index error ignore: [404], }); - res.status(200).json(util.wrapResponse(req.id, { message: 'Delete index request successfully submitted' })); + return res.status(200).json(util.wrapResponse(req.id, { message: 'Delete index request successfully submitted' })); }, ]; From c6eba74116fe7884cb4fd5d403200bfe02442b34 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 14:05:38 +0800 Subject: [PATCH 40/77] Winning submission from challenge 30070732 --- README.md | 27 + config/custom-environment-variables.json | 5 + config/default.json | 2 + migrations/20180910_project_activity.sql | 18 + package-lock.json | 539 ++++++++++++------ package.json | 5 +- src/constants.js | 5 +- src/events/busApi.js | 260 ++++++--- src/models/project.js | 2 + src/routes/admin/project-create-index.js | 7 + src/routes/attachments/create.spec.js | 132 +++-- src/routes/attachments/delete.spec.js | 44 ++ src/routes/attachments/update.js | 1 + src/routes/attachments/update.spec.js | 45 ++ src/routes/milestones/create.spec.js | 55 +- src/routes/milestones/delete.js | 2 + src/routes/milestones/delete.spec.js | 56 +- src/routes/milestones/get.spec.js | 4 + src/routes/milestones/list.spec.js | 4 + src/routes/milestones/update.spec.js | 145 ++++- src/routes/phaseProducts/create.spec.js | 47 +- src/routes/phaseProducts/delete.spec.js | 45 +- src/routes/phaseProducts/get.spec.js | 2 + src/routes/phaseProducts/list.spec.js | 7 +- src/routes/phaseProducts/update.spec.js | 177 ++++++ src/routes/phases/create.spec.js | 55 ++ src/routes/phases/delete.spec.js | 58 +- src/routes/phases/get.spec.js | 2 + src/routes/phases/list.spec.js | 4 + src/routes/phases/update.spec.js | 310 ++++++++++ src/routes/projectMembers/create.spec.js | 148 ++++- src/routes/projectMembers/delete.spec.js | 86 +++ src/routes/projectMembers/update.spec.js | 49 ++ src/routes/projectUpgrade/create.spec.js | 2 + src/routes/projects/create.js | 2 + src/routes/projects/create.spec.js | 3 + src/routes/projects/delete.spec.js | 2 + src/routes/projects/get.spec.js | 4 + src/routes/projects/list-db.js | 1 + src/routes/projects/list-db.spec.js | 75 ++- src/routes/projects/list.js | 1 + src/routes/projects/list.spec.js | 83 ++- src/routes/projects/update.spec.js | 259 +++++++++ src/routes/timelines/create.spec.js | 4 + src/routes/timelines/delete.spec.js | 4 + src/routes/timelines/get.spec.js | 4 + src/routes/timelines/list.spec.js | 4 + src/routes/timelines/update.js | 2 + src/routes/timelines/update.spec.js | 57 +- src/services/index.js | 6 + src/services/kafka/kafkaConsumer.js | 57 ++ src/services/kafka/kafkaConsumer.spec.js | 108 ++++ src/services/kafka/kafkaHandlers.js | 16 + src/services/kafka/updateProjectActivity.js | 49 ++ .../kafka/updateProjectActivity.spec.js | 132 +++++ src/tests/seed.js | 14 + src/tests/util.js | 3 + 57 files changed, 2926 insertions(+), 314 deletions(-) create mode 100644 migrations/20180910_project_activity.sql create mode 100644 src/services/kafka/kafkaConsumer.js create mode 100644 src/services/kafka/kafkaConsumer.spec.js create mode 100644 src/services/kafka/kafkaHandlers.js create mode 100644 src/services/kafka/updateProjectActivity.js create mode 100644 src/services/kafka/updateProjectActivity.spec.js diff --git a/README.md b/README.md index c487aab6..12b2310f 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,33 @@ Run `npm run sync:es` from the root of project to execute the script. **NOTE**: In production these dependencies / services are hosted & managed outside tc-projects-service. +#### Kafka +Kafka must be installed and configured prior starting the application. +Following topics must be created: +``` +notifications.connect.project.updated +notifications.connect.project.files.updated +notifications.connect.project.team.updated +notifications.connect.project.plan.updated +notifications.connect.project.topic.created +notifications.connect.project.topic.updated +notifications.connect.project.post.created +notifications.connect.project.post.edited +``` + +New Kafka related configuration options has been introduced: +``` +"kafkaConfig": { + "hosts": List of Kafka brokers. Default: localhost: 9092 + "clientCert": SSL certificate + "clientCertKey": Certificate key +} +``` +Environment variables: +KAFKA_HOSTS - same as "hosts" +KAFKA_CLIENT_CERT - same as "clientCert" +KAFKA_CLIENT_CERT_KEY - same as "clientCertKey" + ### Test Each of the individual modules/services are unit tested. diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 82606743..41fc3eb3 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -29,6 +29,11 @@ "maxPoolSize": "DB_MAX_POOL_SIZE", "minPoolSize": "DB_MIN_POOL_SIZE" }, + "kafkaConfig": { + "hosts": "KAFKA_HOSTS", + "clientCert": "KAFKA_CLIENT_CERT", + "clientCertKey": "KAFKA_CLIENT_CERT_KEY" + }, "analyticsKey": "SEGMENT_ANALYTICS_KEY", "VALID_ISSUERS": "VALID_ISSUERS", "jwksUri": "JWKS_URI", diff --git a/config/default.json b/config/default.json index e2b2d11d..79440586 100644 --- a/config/default.json +++ b/config/default.json @@ -33,6 +33,8 @@ "minPoolSize": 4, "idleTimeout": 1000 }, + "kafkaConfig": { + }, "analyticsKey": "", "VALID_ISSUERS": "[\"https:\/\/topcoder-newauth.auth0.com\/\",\"https:\/\/api.topcoder-dev.com\"]", "validIssuers": "[\"https:\/\/topcoder-newauth.auth0.com\/\",\"https:\/\/api.topcoder-dev.com\"]", diff --git a/migrations/20180910_project_activity.sql b/migrations/20180910_project_activity.sql new file mode 100644 index 00000000..81fbc727 --- /dev/null +++ b/migrations/20180910_project_activity.sql @@ -0,0 +1,18 @@ +-- +-- UPDATE EXISTING TABLES: +-- projects: +-- added column `lastActivityAt` +-- added column `lastActivityUserId` + +-- +-- projects + +-- Add new columns +ALTER TABLE projects ADD COLUMN "lastActivityAt" timestamp; +ALTER TABLE projects ADD COLUMN "lastActivityUserId" INT; +-- Update new colums +UPDATE projects SET "lastActivityAt"="updatedAt" WHERE "lastActivityAt" is NULL; +UPDATE projects SET "lastActivityUserId"="updatedBy" WHERE "lastActivityUserId" is NULL; +-- Set not null +ALTER TABLE projects ALTER COLUMN "lastActivityAt" SET NOT NULL; +ALTER TABLE projects ALTER COLUMN "lastActivityUserId" SET NOT NULL; diff --git a/package-lock.json b/package-lock.json index 72ec9d9c..228a50d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,23 +13,41 @@ "join-component": "1.1.0" } }, + "@types/bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-JjNHCk6r6aR82aRf2yDtX5NAe8o=" + }, "@types/body-parser": { - "version": "1.16.8", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.16.8.tgz", - "integrity": "sha512-BdN2PXxOFnTXFcyONPW6t0fHjz2fvRZHVMFpaS0wYr+Y8fWEaNOs4V8LEu/fpzQlMx+ahdndgTaGTwPC+J/EeA==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "3.4.32", + "@types/node": "10.9.4" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", "requires": { - "@types/express": "4.0.39", - "@types/node": "8.5.1" + "@types/node": "10.9.4" } }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, "@types/express": { - "version": "4.0.39", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.39.tgz", - "integrity": "sha512-dBUam7jEjyuEofigUXCtublUHknRZvcRgITlGsTbFgPvnTwtQUt2NgLakbsf+PsGo/Nupqr3IXCYsOpBpofyrA==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", "requires": { - "@types/body-parser": "1.16.8", - "@types/express-serve-static-core": "4.0.57", - "@types/serve-static": "1.13.1" + "@types/body-parser": "1.17.0", + "@types/express-serve-static-core": "4.16.0", + "@types/serve-static": "1.13.2" } }, "@types/express-jwt": { @@ -37,16 +55,18 @@ "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.34.tgz", "integrity": "sha1-/b7kxq9cCiRu8qkz9VGZc8dxfwI=", "requires": { - "@types/express": "4.0.39", + "@types/express": "4.16.0", "@types/express-unless": "0.0.32" } }, "@types/express-serve-static-core": { - "version": "4.0.57", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.0.57.tgz", - "integrity": "sha512-QLAHjdLwEICm3thVbXSKRoisjfgMVI4xJH/HU8F385BR2HI7PmM6ax4ELXf8Du6sLmSpySXMYaI+xc//oQ/IFw==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", "requires": { - "@types/node": "8.5.1" + "@types/events": "1.2.0", + "@types/node": "10.9.4", + "@types/range-parser": "1.2.2" } }, "@types/express-unless": { @@ -54,7 +74,7 @@ "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.0.32.tgz", "integrity": "sha512-6YpJyFNlDDnPnRjMOvJCoDYlSDDmG/OEEUsPk7yhNkL4G9hUYtgab6vi1CcWsGSSSM0CsvNlWTG+ywAGnvF03g==", "requires": { - "@types/express": "4.0.39" + "@types/express": "4.16.0" } }, "@types/geojson": { @@ -62,22 +82,32 @@ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==" }, + "@types/lodash": { + "version": "4.14.116", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz", + "integrity": "sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==" + }, "@types/mime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" }, "@types/node": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.1.tgz", - "integrity": "sha512-SrmAO+NhnsuG/6TychSl2VdxBZiw/d6V+8j+DFo8O3PwFi+QeYXWHhAw+b170aSc6zYab6/PjEWRZHIDN9mNUw==" + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.4.tgz", + "integrity": "sha512-fCHV45gS+m3hH17zgkgADUSi2RR1Vht6wOZ0jyHP8rjiQra9f+mIcgwPQHllmDocYOstIEbKlxbFDYlgrTPYqw==" + }, + "@types/range-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", + "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" }, "@types/serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-jDMH+3BQPtvqZVIcsH700Dfi8Q3MIcEx16g/VdxjoqiGR/NntekB10xdBpirMKnPe9z2C5cBmL0vte0YttOr3Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", "requires": { - "@types/express-serve-static-core": "4.0.57", + "@types/express-serve-static-core": "4.16.0", "@types/mime": "2.0.0" } }, @@ -347,9 +377,12 @@ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "2.1.2" + } }, "assert-plus": { "version": "1.0.0", @@ -389,9 +422,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "auth0-js": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.6.0.tgz", - "integrity": "sha1-2a4wFIBzZtO0ecKtGKNTfz4Mlpk=", + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.7.3.tgz", + "integrity": "sha512-iZAqoN4EbsNCS/3VkFPNb4glTyj8hq57T7gcUF+XH8Rua7hBTUzpb101K9zqcdUIBilIdF9XBLCTJ4JGgZ/oFA==", "requires": { "base64-js": "1.2.1", "idtoken-verifier": "1.2.0", @@ -403,11 +436,11 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "formidable": { @@ -420,6 +453,11 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -427,7 +465,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "1.0.2", @@ -454,7 +492,7 @@ "requires": { "component-emitter": "1.2.1", "cookiejar": "2.1.1", - "debug": "3.1.0", + "debug": "3.2.5", "extend": "3.0.1", "form-data": "2.3.1", "formidable": "1.2.1", @@ -481,6 +519,13 @@ "uuid": "3.1.0", "xml2js": "0.4.17", "xmlbuilder": "4.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + } } }, "aws-sign2": { @@ -489,9 +534,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axios": { "version": "0.17.1", @@ -1553,7 +1598,7 @@ }, "babel-runtime": { "version": "6.6.1", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", + "resolved": "http://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", "integrity": "sha1-eIuUtvY04luRvWxd9y1GdFevsAA=", "requires": { "core-js": "2.5.1" @@ -1662,9 +1707,9 @@ "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { "tweetnacl": "0.14.5" @@ -1676,6 +1721,16 @@ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", "dev": true }, + "bin-protocol": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bin-protocol/-/bin-protocol-3.0.4.tgz", + "integrity": "sha1-RlqdNQb+sOEmtStbIWDZNuFbJ/Q=", + "requires": { + "lodash": "4.17.4", + "long": "3.2.0", + "protocol-buffers-schema": "3.3.2" + } + }, "binary-extensions": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", @@ -1717,14 +1772,6 @@ "type-is": "1.6.15" } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.2.0" - } - }, "boxen": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.2.2.tgz", @@ -1851,6 +1898,11 @@ } } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -1943,6 +1995,15 @@ "type-detect": "1.0.0" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "1.0.2" + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -1955,6 +2016,12 @@ "supports-color": "2.0.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "check-more-types": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.3.0.tgz", @@ -2201,6 +2268,11 @@ "xdg-basedir": "3.0.0" } }, + "connection-parse": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/connection-parse/-/connection-parse-0.0.7.tgz", + "integrity": "sha1-GOcxiqsGppkmc3KxDFIm0locmmk=" + }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -2298,24 +2370,6 @@ } } }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.0" - } - } - } - }, "crypto-browserify": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", @@ -2563,12 +2617,13 @@ "dev": true }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "ecdsa-sig-formatter": { @@ -3113,7 +3168,7 @@ "resolved": "https://registry.npmjs.org/express-request-id/-/express-request-id-1.4.0.tgz", "integrity": "sha1-J3ssCUmAPmgQTJ1Fw+aJNPlr9aI=", "requires": { - "uuid": "3.1.0" + "uuid": "3.3.2" } }, "express-sanitizer": { @@ -3162,9 +3217,9 @@ } }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -3907,15 +3962,6 @@ "dev": true, "optional": true }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -3926,6 +3972,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "strip-ansi": { "version": "3.0.1", "bundled": true, @@ -4435,21 +4490,21 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "requires": { - "ajv": "5.5.1", + "ajv": "5.5.2", "har-schema": "2.0.0" }, "dependencies": { "ajv": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", - "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { "co": "4.6.0", - "fast-deep-equal": "1.0.0", + "fast-deep-equal": "1.1.0", "fast-json-stable-stringify": "2.0.0", "json-schema-traverse": "0.3.1" } @@ -4488,15 +4543,13 @@ "sparkles": "1.0.0" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "hashring": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/hashring/-/hashring-3.2.0.tgz", + "integrity": "sha1-/aTv3oqiLNuX+x0qZeiEAeHBRM4=", "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" + "connection-parse": "0.0.7", + "simple-lru-cache": "0.0.2" } }, "hoek": { @@ -4555,7 +4608,7 @@ "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.13.1" + "sshpk": "1.14.2" } }, "iconv-lite": { @@ -4576,11 +4629,11 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "formidable": { @@ -4593,6 +4646,11 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -4600,7 +4658,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "1.0.2", @@ -4627,7 +4685,7 @@ "requires": { "component-emitter": "1.2.1", "cookiejar": "2.1.1", - "debug": "3.1.0", + "debug": "3.2.5", "extend": "3.0.1", "form-data": "2.3.1", "formidable": "1.2.1", @@ -5385,16 +5443,16 @@ } }, "jwks-rsa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.2.1.tgz", - "integrity": "sha512-xg+fw7FOV4eGdDIEMqQJvPLmFv85h4uN+j/GKwJZAxlCrDQpM8ov1F709xKGEp/dG3l4TUxoSOeN6YK7+KpinQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.3.0.tgz", + "integrity": "sha512-9q+d5VffK/FvFAjuXoddrq7zQybFSINV4mcwJJExGKXGyjWWpTt3vsn/aX33aB0heY02LK0qSyicdtRK0gVTig==", "requires": { "@types/express-jwt": "0.0.34", "debug": "2.6.9", - "limiter": "1.1.2", - "lru-memoizer": "1.11.1", + "limiter": "1.1.3", + "lru-memoizer": "1.12.0", "ms": "2.0.0", - "request": "2.83.0" + "request": "2.88.0" } }, "jws": { @@ -5461,7 +5519,7 @@ "dependencies": { "lodash": { "version": "3.9.3", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.9.3.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.9.3.tgz", "integrity": "sha1-AVnoaDL+/8bWHYUrEqlTuZSWvTI=" }, "semver": { @@ -5482,12 +5540,19 @@ } }, "libpq": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/libpq/-/libpq-1.8.7.tgz", - "integrity": "sha1-wt6xIeKPf4S9OyRRr/9otmY+dPk=", + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/libpq/-/libpq-1.8.8.tgz", + "integrity": "sha512-0TVzqkbAZZiM8JJy5sagRyXOkvU9zTBlgGX6YdzuWECobc5F81Tp6uuS+djMZrnB5YN4O/ff52hsvXYBRW2gdQ==", "requires": { "bindings": "1.2.1", - "nan": "2.7.0" + "nan": "2.11.0" + }, + "dependencies": { + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" + } } }, "liftoff": { @@ -5522,9 +5587,9 @@ } }, "limiter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.2.tgz", - "integrity": "sha512-JIKZ0xb6fZZYa3deZ0BgXCgX6HgV8Nx3mFGeFHmFWW8Fb2c08e0CyE+G3nalpD0xGvGssjGb1UdFr+PprxZEbw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.3.tgz", + "integrity": "sha512-zrycnIMsLw/3ZxTbW7HCez56rcFGecWTx5OZNplzcXUUmJLmoYArC6qdJzmAN5BWiNXGcpjhF9RQ1HSv5zebEw==" }, "load-json-file": { "version": "2.0.0", @@ -5791,6 +5856,11 @@ "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=", "dev": true }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -5819,21 +5889,16 @@ "dev": true }, "lru-memoizer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-1.11.1.tgz", - "integrity": "sha1-BpP2EAWTkUwC4ZK/m42TiEy/UNM=", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-1.12.0.tgz", + "integrity": "sha1-7+ZXBsyKnMZT+A8NWm6jitlQ41I=", "requires": { "lock": "0.1.4", - "lodash": "4.5.1", + "lodash": "4.17.4", "lru-cache": "4.0.2", "very-fast-args": "1.1.0" }, "dependencies": { - "lodash": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.5.1.tgz", - "integrity": "sha1-gOigdMpfOJOmscELKmNkktcQwxY=" - }, "lru-cache": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", @@ -6114,6 +6179,11 @@ "duplexer2": "0.0.2" } }, + "murmur-hash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmur-hash-js/-/murmur-hash-js-1.0.0.tgz", + "integrity": "sha1-UEEEkmnJZjPIZjhpYLL0KJ515bA=" + }, "mute-stream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", @@ -6165,6 +6235,38 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "nice-simple-logger": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nice-simple-logger/-/nice-simple-logger-1.0.1.tgz", + "integrity": "sha1-D55khSe+e+PkmrdvqMjAmK+VG/Y=", + "requires": { + "lodash": "4.17.4" + } + }, + "no-kafka": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/no-kafka/-/no-kafka-3.2.10.tgz", + "integrity": "sha1-0sq8QwZbSS24wVyiOK6V8WgIGvU=", + "requires": { + "@types/bluebird": "3.5.0", + "@types/lodash": "4.14.116", + "bin-protocol": "3.0.4", + "bluebird": "3.5.1", + "buffer-crc32": "0.2.13", + "hashring": "3.2.0", + "lodash": "4.17.5", + "murmur-hash-js": "1.0.0", + "nice-simple-logger": "1.0.1", + "wrr-pool": "1.1.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" + } + } + }, "nodemon": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.12.1.tgz", @@ -6229,9 +6331,9 @@ "dev": true }, "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -6586,7 +6688,7 @@ "resolved": "https://registry.npmjs.org/pg-native/-/pg-native-1.10.1.tgz", "integrity": "sha1-lOYcy7hafzQ2suUmMVx1gRB/5Aw=", "requires": { - "libpq": "1.8.7", + "libpq": "1.8.8", "pg-types": "1.6.0", "readable-stream": "1.0.31" }, @@ -6747,6 +6849,11 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "protocol-buffers-schema": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", + "integrity": "sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w==" + }, "proxy-addr": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", @@ -6770,6 +6877,11 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -7129,32 +7241,78 @@ "dev": true }, "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "0.7.0", - "aws4": "1.6.0", + "aws4": "1.8.0", "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", + "combined-stream": "1.0.6", + "extend": "3.0.2", "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", + "form-data": "2.3.2", + "har-validator": "5.1.0", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", + "mime-types": "2.1.20", + "oauth-sign": "0.9.0", "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "uuid": "3.3.2" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.20" + } + }, + "mime-db": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" + }, + "mime-types": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "requires": { + "mime-db": "1.36.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } } }, "require-directory": { @@ -7267,6 +7425,11 @@ "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", "optional": true }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "samsam": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", @@ -7342,7 +7505,7 @@ "shimmer": "1.1.0", "terraformer-wkt-parser": "1.1.2", "toposort-class": "1.0.1", - "uuid": "3.1.0", + "uuid": "3.3.2", "validator": "5.7.0", "wkx": "0.2.0" }, @@ -7553,6 +7716,11 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-lru-cache": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/simple-lru-cache/-/simple-lru-cache-0.0.2.tgz", + "integrity": "sha1-1ZzDoZPBpdAyD4Tucy9uRxPlEd0=" + }, "sinon": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", @@ -7592,14 +7760,6 @@ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "requires": { - "hoek": "4.2.0" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -7657,17 +7817,18 @@ "dev": true }, "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "0.2.3", + "asn1": "0.2.4", "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", + "bcrypt-pbkdf": "1.0.2", "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", + "ecc-jsbn": "0.1.2", "getpass": "0.1.7", "jsbn": "0.1.1", + "safer-buffer": "2.1.2", "tweetnacl": "0.14.5" } }, @@ -7690,11 +7851,6 @@ "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=" }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -7706,10 +7862,10 @@ "strip-ansi": "3.0.1" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "strip-ansi": { "version": "3.0.1", @@ -7922,12 +8078,12 @@ "tc-core-library-js": { "version": "github:appirio-tech/tc-core-library-js#df1f5c1a5578d3d1e475bfb4a7413d9dec25525a", "requires": { - "auth0-js": "9.6.0", + "auth0-js": "9.7.3", "axios": "0.12.0", "bunyan": "1.8.12", "config": "1.27.0", "jsonwebtoken": "7.4.3", - "jwks-rsa": "1.2.1", + "jwks-rsa": "1.3.0", "le_node": "1.7.1", "lodash": "4.17.4", "millisecond": "0.1.2" @@ -7935,7 +8091,7 @@ "dependencies": { "axios": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.12.0.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.12.0.tgz", "integrity": "sha1-uQewIhzDTsHJ+sGOx/B935V4W6Q=", "requires": { "follow-redirects": "0.0.7" @@ -7943,7 +8099,7 @@ }, "follow-redirects": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", + "resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=", "requires": { "debug": "2.6.9", @@ -7962,7 +8118,7 @@ }, "joi": { "version": "6.10.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "resolved": "http://registry.npmjs.org/joi/-/joi-6.10.1.tgz", "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", "requires": { "hoek": "2.16.3", @@ -8148,10 +8304,11 @@ } }, "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { + "psl": "1.1.29", "punycode": "1.4.1" }, "dependencies": { @@ -8418,9 +8575,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8flags": { "version": "2.1.1", @@ -8642,6 +8799,14 @@ "signal-exit": "3.0.2" } }, + "wrr-pool": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wrr-pool/-/wrr-pool-1.1.3.tgz", + "integrity": "sha1-/a0i8uofMDY//l14HPeUl6d/8H4=", + "requires": { + "lodash": "4.17.4" + } + }, "xdg-basedir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", diff --git a/package.json b/package.json index eab997b8..0eda36d5 100644 --- a/package.json +++ b/package.json @@ -55,12 +55,14 @@ "memwatch-next": "^0.3.0", "method-override": "^2.3.9", "moment": "^2.22.2", + "no-kafka": "^3.2.10", "pg": "^4.5.5", "pg-native": "^1.10.1", "sequelize": "^3.23.0", "tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.3", "traverse": "^0.6.6", - "urlencode": "^1.1.0" + "urlencode": "^1.1.0", + "uuid": "^3.3.2" }, "devDependencies": { "babel-cli": "^6.9.0", @@ -71,6 +73,7 @@ "babel-preset-es2015": "^6.9.0", "bunyan": "^1.8.1", "chai": "^3.5.0", + "chai-as-promised": "^7.1.1", "eslint": "^3.16.1", "eslint-config-airbnb-base": "^11.1.0", "eslint-plugin-import": "^2.2.0", diff --git a/src/constants.js b/src/constants.js index 99ca4e5d..c9af6d95 100644 --- a/src/constants.js +++ b/src/constants.js @@ -64,6 +64,7 @@ export const EVENT = { export const BUS_API_EVENT = { PROJECT_CREATED: 'notifications.connect.project.created', + PROJECT_UPDATED: 'notifications.connect.project.updated', PROJECT_SUBMITTED_FOR_REVIEW: 'notifications.connect.project.submittedForReview', PROJECT_APPROVED: 'notifications.connect.project.approved', PROJECT_PAUSED: 'notifications.connect.project.paused', @@ -88,11 +89,13 @@ export const BUS_API_EVENT = { PROJECT_FILE_UPLOADED: 'notifications.connect.project.fileUploaded', PROJECT_SPECIFICATION_MODIFIED: 'notifications.connect.project.specificationModified', PROJECT_PROGRESS_MODIFIED: 'notifications.connect.project.progressModified', + PROJECT_FILES_UPDATED: 'notifications.connect.project.files.updated', + PROJECT_TEAM_UPDATED: 'notifications.connect.project.team.updated', // When phase is added/updated/deleted from the project, // When product is added/deleted from a phase // When product is updated on any field other than specification - PROJECT_PLAN_MODIFIED: 'notifications.connect.project.planModified', + PROJECT_PLAN_UPDATED: 'notifications.connect.project.plan.updated', PROJECT_PLAN_READY: 'notifications.connect.project.planReady', diff --git a/src/events/busApi.js b/src/events/busApi.js index 2c8481f6..d93d07ab 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -93,6 +93,19 @@ module.exports = (app, logger) => { initiatorUserId: req.authUser.userId, }, 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))) { + createEvent(BUS_API_EVENT.PROJECT_UPDATED, { + projectId: updated.id, + projectName: updated.name, + projectUrl: connectProjectUrl(updated.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } }); /** @@ -126,6 +139,14 @@ module.exports = (app, logger) => { userId: member.userId, initiatorUserId: req.authUser.userId, }, logger); + + createEvent(BUS_API_EVENT.PROJECT_TEAM_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); }).catch(err => null); // eslint-disable-line no-unused-vars }); @@ -155,6 +176,14 @@ module.exports = (app, logger) => { userId: member.userId, initiatorUserId: req.authUser.userId, }, logger); + + createEvent(BUS_API_EVENT.PROJECT_TEAM_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); } }).catch(err => null); // eslint-disable-line no-unused-vars }); @@ -166,12 +195,13 @@ module.exports = (app, logger) => { logger.debug('receive PROJECT_MEMBER_UPDATED event'); const projectId = _.parseInt(req.params.projectId); - if (updated.isPrimary && !original.isPrimary) { - models.Project.findOne({ - where: { id: projectId }, - }) - .then((project) => { - if (project) { + + models.Project.findOne({ + where: { id: projectId }, + }) + .then((project) => { + if (project) { + if (updated.isPrimary && !original.isPrimary) { createEvent(BUS_API_EVENT.MEMBER_ASSIGNED_AS_OWNER, { projectId, projectName: project.name, @@ -180,8 +210,16 @@ module.exports = (app, logger) => { initiatorUserId: req.authUser.userId, }, logger); } - }).catch(err => null); // eslint-disable-line no-unused-vars - } + + createEvent(BUS_API_EVENT.PROJECT_TEAM_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars }); /** @@ -205,9 +243,62 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); + + createEvent(BUS_API_EVENT.PROJECT_FILES_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); }).catch(err => null); // eslint-disable-line no-unused-vars }); + + /** + * PROJECT_ATTACHMENT_UPDATED + */ + app.on(EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_UPDATED, ({ req }) => { + logger.debug('receive PROJECT_ATTACHMENT_UPDATED event'); + + const projectId = _.parseInt(req.params.projectId); + + models.Project.findOne({ + where: { id: projectId }, + }) + .then((project) => { + createEvent(BUS_API_EVENT.PROJECT_FILES_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + }).catch(err => null); // eslint-disable-line no-unused-vars + }); + + /** + * PROJECT_ATTACHMENT_REMOVED + */ + app.on(EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED, ({ req }) => { + logger.debug('receive PROJECT_ATTACHMENT_REMOVED event'); + + const projectId = _.parseInt(req.params.projectId); + + models.Project.findOne({ + where: { id: projectId }, + }) + .then((project) => { + createEvent(BUS_API_EVENT.PROJECT_FILES_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + }).catch(err => null); // eslint-disable-line no-unused-vars + }); + /** * If the project is in draft status and the phase is in reviewed status, and it's the * only phase in the project with that status, then send the plan ready event. @@ -249,14 +340,13 @@ module.exports = (app, logger) => { where: { id: projectId }, }) .then((project) => { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); - return sendPlanReadyEventIfNeeded(req, project, created); }).catch(err => null); // eslint-disable-line no-unused-vars }); @@ -273,7 +363,7 @@ module.exports = (app, logger) => { where: { id: projectId }, }) .then((project) => { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), @@ -296,13 +386,18 @@ module.exports = (app, logger) => { where: { id: projectId }, }) .then((project) => { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { - projectId, - projectName: project.name, - projectUrl: connectProjectUrl(projectId), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); + // send PROJECT_PLAN_UPDATED Kafka message when one of the specified below properties changed + const watchProperties = ['spentBudget', 'progress', 'details', 'status', 'budget', 'startDate', 'duration']; + if (!_.isEqual(_.pick(original, watchProperties), + _.pick(updated, watchProperties))) { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId, + projectName: project.name, + projectUrl: connectProjectUrl(projectId), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } [ ['spentBudget', BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT], @@ -331,51 +426,6 @@ module.exports = (app, logger) => { }).catch(err => null); // eslint-disable-line no-unused-vars }); - /** - * PROJECT_PHASE_PRODUCT_ADDED - */ - app.on(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_ADDED, ({ req, created }) => { // eslint-disable-line no-unused-vars - logger.debug('receive PROJECT_PHASE_PRODUCT_ADDED event'); - - const projectId = _.parseInt(req.params.projectId); - - models.Project.findOne({ - where: { id: projectId }, - }) - .then((project) => { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { - projectId, - projectName: project.name, - projectUrl: connectProjectUrl(projectId), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - phase: created, - }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars - }); - - /** - * PROJECT_PHASE_PRODUCT_REMOVED - */ - app.on(EVENT.ROUTING_KEY.PROJECT_PHASE_PRODUCT_REMOVED, ({ req, deleted }) => { // eslint-disable-line no-unused-vars - logger.debug('receive PROJECT_PHASE_PRODUCT_REMOVED event'); - - const projectId = _.parseInt(req.params.projectId); - - models.Project.findOne({ - where: { id: projectId }, - }) - .then((project) => { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { - projectId, - projectName: project.name, - projectUrl: connectProjectUrl(projectId), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - }).catch(err => null); // eslint-disable-line no-unused-vars - }); - /** * PROJECT_PHASE_PRODUCT_UPDATED */ @@ -401,11 +451,10 @@ module.exports = (app, logger) => { }, logger); } - // Other fields change - const originalWithouDetails = _.omit(original, 'details'); - const updatedWithouDetails = _.omit(updated, 'details'); - if (!_.isEqual(originalWithouDetails, updatedWithouDetails)) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_MODIFIED, { + const watchProperties = ['name', 'estimatedPrice', 'actualPrice', 'details']; + if (!_.isEqual(_.pick(original, watchProperties), + _.pick(updated, watchProperties))) { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), @@ -479,7 +528,18 @@ module.exports = (app, logger) => { models.Project.findOne({ where: { id: projectId }, }) - .then(project => sendMilestoneNotification(req, {}, created, project)) + .then((project) => { + if (project) { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId, + projectName: project.name, + projectUrl: connectProjectUrl(projectId), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + sendMilestoneNotification(req, {}, created, project); + }) .catch(err => null); // eslint-disable-line no-unused-vars }); @@ -496,6 +556,18 @@ module.exports = (app, logger) => { where: { id: projectId }, }) .then((project) => { + // send PROJECT_UPDATED Kafka message when one of the specified below properties changed + const watchProperties = ['startDate', 'endDate', 'duration', 'details', 'status', 'order']; + if (!_.isEqual(_.pick(original, watchProperties), + _.pick(updated, watchProperties))) { + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId, + projectName: project.name, + projectUrl: connectProjectUrl(projectId), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } sendMilestoneNotification(req, original, updated, project); logger.debug('cascadedUpdates', cascadedUpdates); @@ -525,4 +597,54 @@ module.exports = (app, logger) => { }) .catch(err => null); // eslint-disable-line no-unused-vars }); + + /** + * MILESTONE_REMOVED. + */ + app.on(EVENT.ROUTING_KEY.MILESTONE_REMOVED, ({ req }) => { + logger.debug('receive MILESTONE_REMOVED event'); + // 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(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId, + projectName: project.name, + projectUrl: connectProjectUrl(projectId), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars + }); + + app.on(EVENT.ROUTING_KEY.TIMELINE_UPDATED, ({ req, original, updated }) => { + logger.debug('receive TIMELINE_UPDATED event'); + // 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))) { + // 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(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId, + projectName: project.name, + projectUrl: connectProjectUrl(projectId), + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); + } + }).catch(err => null); // eslint-disable-line no-unused-vars + } + }); }; diff --git a/src/models/project.js b/src/models/project.js index 6bb6b66f..f7d52128 100644 --- a/src/models/project.js +++ b/src/models/project.js @@ -42,6 +42,8 @@ module.exports = function defineProject(sequelize, DataTypes) { createdBy: { type: DataTypes.INTEGER, allowNull: false }, updatedBy: { type: DataTypes.INTEGER, allowNull: false }, version: { type: DataTypes.STRING(3), allowNull: false, defaultValue: 'v3' }, + lastActivityAt: { type: DataTypes.DATE, allowNull: false }, + lastActivityUserId: { type: DataTypes.INTEGER, allowNull: false }, }, { tableName: 'projects', paranoid: true, diff --git a/src/routes/admin/project-create-index.js b/src/routes/admin/project-create-index.js index e03a6e2b..6cc27247 100644 --- a/src/routes/admin/project-create-index.js +++ b/src/routes/admin/project-create-index.js @@ -253,6 +253,13 @@ function getRequestBody(indexName, docType) { updatedBy: { type: 'integer', }, + lastActivityAt: { + type: 'date', + format: 'strict_date_optional_time||epoch_millis', + }, + lastActivityUserId: { + type: 'long', + }, userId: { type: 'long', }, diff --git a/src/routes/attachments/create.spec.js b/src/routes/attachments/create.spec.js index 2aff8b54..96617792 100644 --- a/src/routes/attachments/create.spec.js +++ b/src/routes/attachments/create.spec.js @@ -6,6 +6,8 @@ import server from '../../app'; import models from '../../models'; import util from '../../util'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -17,9 +19,46 @@ const body = { s3Bucket: 'submissions-staging-dev', contentType: 'application/pdf', }; + describe('Project Attachments', () => { let project1; - before((done) => { + let postSpy; + let getSpy; + let stub; + let sandbox; + + beforeEach((done) => { + const mockHttpClient = { + defaults: { headers: { common: {} } }, + post: () => new Promise(resolve => resolve({ + status: 200, + data: { + status: 200, + result: { + success: true, + status: 200, + content: { + filePath: 'tmp/spec.pdf', + preSignedURL: 'www.topcoder.com/media/spec.pdf', + }, + }, + }, + })), + get: () => new Promise(resolve => resolve({ + status: 200, + data: { + result: { + success: true, + status: 200, + content: { + filePath: 'tmp/spec.pdf', + preSignedURL: 'http://topcoder-media.s3.amazon.com/projects/1/spec.pdf', + }, + }, + }, + })), + }; + // mocks testUtil.clearDb() .then(() => { @@ -32,6 +71,8 @@ describe('Project Attachments', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -42,12 +83,20 @@ describe('Project Attachments', () => { isPrimary: true, createdBy: 1, updatedBy: 1, - }).then(() => 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(); + }); }); }); }); - after((done) => { + afterEach((done) => { + sandbox.restore(); testUtil.clearDb(done); }); @@ -64,41 +113,6 @@ describe('Project Attachments', () => { }); it('should return 201 return attachment record', (done) => { - const mockHttpClient = { - defaults: { headers: { common: {} } }, - post: () => new Promise(resolve => resolve({ - status: 200, - data: { - status: 200, - result: { - success: true, - status: 200, - content: { - filePath: 'tmp/spec.pdf', - preSignedURL: 'www.topcoder.com/media/spec.pdf', - }, - }, - }, - })), - get: () => new Promise(resolve => resolve({ - status: 200, - data: { - result: { - success: true, - status: 200, - content: { - filePath: 'tmp/spec.pdf', - preSignedURL: 'http://topcoder-media.s3.amazon.com/projects/1/spec.pdf', - }, - }, - }, - })), - }; - const postSpy = sinon.spy(mockHttpClient, 'post'); - const getSpy = sinon.spy(mockHttpClient, 'get'); - const stub = sinon.stub(util, 'getHttpClient', () => mockHttpClient); - // mock util s3FileTransfer - util.s3FileTransfer = () => Promise.resolve(true); request(server) .post(`/v4/projects/${project1.id}/attachments/`) .set({ @@ -123,5 +137,47 @@ describe('Project Attachments', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_FILES_UPDATED message when attachment added', (done) => { + request(server) + .post(`/v4/projects/${project1.id}/attachments/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ param: body }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + // Wait for app message handler to complete + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_FILE_UPLOADED); + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_FILES_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(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/attachments/delete.spec.js b/src/routes/attachments/delete.spec.js index 850b8ca1..7b26193b 100644 --- a/src/routes/attachments/delete.spec.js +++ b/src/routes/attachments/delete.spec.js @@ -8,6 +8,8 @@ import models from '../../models'; import util from '../../util'; import server from '../../app'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; describe('Project Attachments delete', () => { @@ -26,6 +28,8 @@ describe('Project Attachments delete', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -142,5 +146,45 @@ describe('Project Attachments delete', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_FILES_UPDATED message when attachment deleted', (done) => { + request(server) + .delete(`/v4/projects/${project1.id}/attachments/${attachment.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + // Wait for app message handler to complete + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_FILES_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(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/attachments/update.js b/src/routes/attachments/update.js index 8bebd832..9a67a86a 100644 --- a/src/routes/attachments/update.js +++ b/src/routes/attachments/update.js @@ -62,6 +62,7 @@ module.exports = [ { original: previousValue, updated: updated.get({ plain: true }) }, { correlationId: req.id }, ); + req.app.emit(EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_UPDATED, { req, original: previousValue, updated }); }).catch(err => next(err))); }, ]; diff --git a/src/routes/attachments/update.spec.js b/src/routes/attachments/update.spec.js index 3735eca2..c181392f 100644 --- a/src/routes/attachments/update.spec.js +++ b/src/routes/attachments/update.spec.js @@ -6,6 +6,8 @@ import request from 'supertest'; import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -25,6 +27,8 @@ describe('Project Attachments update', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -106,5 +110,46 @@ describe('Project Attachments update', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.stub(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_FILES_UPDATED message when attachment updated', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}/attachments/${attachment.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ param: { title: 'updated title', description: 'updated description' } }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + // Wait for app message handler to complete + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_FILES_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(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/milestones/create.spec.js b/src/routes/milestones/create.spec.js index 250e8175..dba44d71 100644 --- a/src/routes/milestones/create.spec.js +++ b/src/routes/milestones/create.spec.js @@ -1,13 +1,16 @@ +/* eslint-disable no-unused-expressions */ /** * Tests for create.js */ import chai from 'chai'; +import sinon from 'sinon'; import request from 'supertest'; import _ from 'lodash'; import server from '../../app'; import testUtil from '../../tests/util'; import models from '../../models'; -import { EVENT } from '../../constants'; +import busApi from '../../services/busApi'; +import { EVENT, BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -28,6 +31,8 @@ describe('CREATE milestone', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -38,6 +43,8 @@ describe('CREATE milestone', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ], { returning: true }) @@ -604,5 +611,51 @@ describe('CREATE milestone', () => { done(); }); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone created', (done) => { + request(server) + .post('/v4/timelines/1/milestones') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(body) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/milestones/delete.js b/src/routes/milestones/delete.js index 50377c90..45e5a41b 100644 --- a/src/routes/milestones/delete.js +++ b/src/routes/milestones/delete.js @@ -55,6 +55,8 @@ module.exports = [ deleted, { correlationId: req.id }, ); + req.app.emit(EVENT.ROUTING_KEY.MILESTONE_REMOVED, + { req, deleted }); // Write to response res.status(204).end(); diff --git a/src/routes/milestones/delete.spec.js b/src/routes/milestones/delete.spec.js index a82294e9..dbde7dab 100644 --- a/src/routes/milestones/delete.spec.js +++ b/src/routes/milestones/delete.spec.js @@ -1,14 +1,16 @@ +/* eslint-disable no-unused-expressions */ /** * Tests for delete.js */ import request from 'supertest'; +import sinon from 'sinon'; import chai from 'chai'; import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; -import { EVENT } from '../../constants'; - +import { EVENT, BUS_API_EVENT } from '../../constants'; +import busApi from '../../services/busApi'; const expectAfterDelete = (timelineId, id, err, next) => { if (err) throw err; @@ -50,6 +52,8 @@ describe('DELETE milestone', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -60,6 +64,8 @@ describe('DELETE milestone', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) @@ -348,5 +354,51 @@ describe('DELETE milestone', () => { .expect(204) .end(err => expectAfterDelete(1, 1, err, done)); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + // not testing fields separately as startDate is required parameter, + // thus PROJECT_PLAN_UPDATED will be always sent + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone removed', (done) => { + request(server) + .delete('/v4/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/milestones/get.spec.js b/src/routes/milestones/get.spec.js index 919b756d..39ede27d 100644 --- a/src/routes/milestones/get.spec.js +++ b/src/routes/milestones/get.spec.js @@ -24,6 +24,8 @@ describe('GET milestone', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -34,6 +36,8 @@ describe('GET milestone', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/milestones/list.spec.js b/src/routes/milestones/list.spec.js index 0240ee43..56b48d0c 100644 --- a/src/routes/milestones/list.spec.js +++ b/src/routes/milestones/list.spec.js @@ -94,6 +94,8 @@ describe('LIST timelines', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -104,6 +106,8 @@ describe('LIST timelines', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/milestones/update.spec.js b/src/routes/milestones/update.spec.js index d6b77bb9..15ac4eb0 100644 --- a/src/routes/milestones/update.spec.js +++ b/src/routes/milestones/update.spec.js @@ -1,14 +1,17 @@ +/* eslint-disable no-unused-expressions */ /** * Tests for get.js */ import chai from 'chai'; +import sinon from 'sinon'; import request from 'supertest'; import moment from 'moment'; import _ from 'lodash'; import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; -import { EVENT, MILESTONE_STATUS } from '../../constants'; +import busApi from '../../services/busApi'; +import { EVENT, MILESTONE_STATUS, BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -26,6 +29,8 @@ describe('UPDATE Milestone', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -36,6 +41,8 @@ describe('UPDATE Milestone', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) @@ -1077,5 +1084,141 @@ describe('UPDATE Milestone', () => { .expect(200) .end(done); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone duration updated', (done) => { + request(server) + .patch('/v4/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + duration: 1, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + createEventSpy.secondCall.calledWith(BUS_API_EVENT.TIMELINE_MODIFIED); + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone status updated', (done) => { + request(server) + .patch('/v4/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + status: 'reviewed', + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone order updated', (done) => { + request(server) + .patch('/v4/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + order: 2, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone plannedText updated', (done) => { + request(server) + .patch('/v4/timelines/1/milestones/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + plannedText: 'new text', + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phaseProducts/create.spec.js b/src/routes/phaseProducts/create.spec.js index e5fe049d..e3e23f47 100644 --- a/src/routes/phaseProducts/create.spec.js +++ b/src/routes/phaseProducts/create.spec.js @@ -1,10 +1,12 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; +import sinon from 'sinon'; import chai from 'chai'; import request from 'supertest'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; const should = chai.should(); @@ -35,7 +37,7 @@ describe('Phase Products', () => { lastName: 'lName', email: 'some@abc.com', }; - before((done) => { + beforeEach((done) => { // mocks testUtil.clearDb() .then(() => { @@ -48,6 +50,8 @@ describe('Phase Products', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; // create members @@ -90,7 +94,7 @@ describe('Phase Products', () => { }); }); - after((done) => { + afterEach((done) => { testUtil.clearDb(done); }); @@ -215,5 +219,44 @@ describe('Phase Products', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when product phase created', (done) => { + request(server) + .post(`/v4/projects/${projectId}/phases/${phaseId}/products`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: body }) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phaseProducts/delete.spec.js b/src/routes/phaseProducts/delete.spec.js index 5f958f4d..99c62051 100644 --- a/src/routes/phaseProducts/delete.spec.js +++ b/src/routes/phaseProducts/delete.spec.js @@ -1,10 +1,12 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; +import sinon from 'sinon'; import request from 'supertest'; import chai from 'chai'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; const expectAfterDelete = (projectId, phaseId, id, err, next) => { if (err) throw err; @@ -63,7 +65,7 @@ describe('Phase Products', () => { lastName: 'lName', email: 'some@abc.com', }; - before((done) => { + beforeEach((done) => { // mocks testUtil.clearDb() .then(() => { @@ -76,6 +78,8 @@ describe('Phase Products', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; // create members @@ -123,7 +127,7 @@ describe('Phase Products', () => { }); }); - after((done) => { + afterEach((done) => { testUtil.clearDb(done); }); @@ -187,5 +191,42 @@ describe('Phase Products', () => { .expect(204) .end(err => expectAfterDelete(projectId, phaseId, productId, err, done)); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when product phase removed', (done) => { + request(server) + .delete(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phaseProducts/get.spec.js b/src/routes/phaseProducts/get.spec.js index d2022f17..b0fd774e 100644 --- a/src/routes/phaseProducts/get.spec.js +++ b/src/routes/phaseProducts/get.spec.js @@ -51,6 +51,8 @@ describe('Phase Products', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phaseProducts/list.spec.js b/src/routes/phaseProducts/list.spec.js index 0b0b46db..566c63a7 100644 --- a/src/routes/phaseProducts/list.spec.js +++ b/src/routes/phaseProducts/list.spec.js @@ -57,6 +57,8 @@ describe('Phase Products', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; project = p.toJSON(); @@ -94,12 +96,13 @@ describe('Phase Products', () => { }).then((phase) => { phaseId = phase.id; _.assign(body, { phaseId, projectId }); - + project.lastActivityAt = 1; project.phases = [phase.toJSON()]; models.PhaseProduct.create(body).then((product) => { project.phases[0].products = [product.toJSON()]; - + // Overwrite lastActivityAt as otherwise ES fill not be able to parse it + project.lastActivityAt = 1; // Index to ES return server.services.es.index({ index: ES_PROJECT_INDEX, diff --git a/src/routes/phaseProducts/update.spec.js b/src/routes/phaseProducts/update.spec.js index 1f0d91ac..37fbe09d 100644 --- a/src/routes/phaseProducts/update.spec.js +++ b/src/routes/phaseProducts/update.spec.js @@ -1,10 +1,13 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; import chai from 'chai'; +import sinon from 'sinon'; import request from 'supertest'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -61,6 +64,8 @@ describe('Phase Products', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; // create members @@ -208,5 +213,177 @@ describe('Phase Products', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when name updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + name: 'new name', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when estimatedPrice updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + estimatedPrice: 123, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when actualPrice updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + actualPrice: 123, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when details updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + details: 'something', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PRODUCT_SPECIFICATION_MODIFIED); + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when type updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}/products/${productId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + type: 'another type', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index 2f84160f..49c61924 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -1,10 +1,15 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; import chai from 'chai'; +import sinon from 'sinon'; import request from 'supertest'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { + BUS_API_EVENT, +} from '../../constants'; const should = chai.should(); @@ -33,6 +38,7 @@ const validatePhase = (resJson, expectedPhase) => { describe('Project Phases', () => { let projectId; + let projectName; const memberUser = { handle: testUtil.getDecodedToken(testUtil.jwts.member).handle, userId: testUtil.getDecodedToken(testUtil.jwts.member).userId, @@ -61,8 +67,11 @@ describe('Project Phases', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; + projectName = p.name; // create members models.ProjectMember.bulkCreate([{ id: 1, @@ -336,5 +345,51 @@ describe('Project Phases', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when phase added', (done) => { + request(server) + .post(`/v4/projects/${projectId}/phases/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ param: body }) + .expect('Content-Type', /json/) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phases/delete.spec.js b/src/routes/phases/delete.spec.js index 1b3ace93..b180d6b9 100644 --- a/src/routes/phases/delete.spec.js +++ b/src/routes/phases/delete.spec.js @@ -1,10 +1,16 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; import request from 'supertest'; +import sinon from 'sinon'; import chai from 'chai'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; + +import { + BUS_API_EVENT, +} from '../../constants'; const expectAfterDelete = (projectId, id, err, next) => { if (err) throw err; @@ -48,6 +54,7 @@ const body = { describe('Project Phases', () => { let projectId; + let projectName; let phaseId; const memberUser = { handle: testUtil.getDecodedToken(testUtil.jwts.member).handle, @@ -63,7 +70,7 @@ describe('Project Phases', () => { lastName: 'lName', email: 'some@abc.com', }; - before((done) => { + beforeEach((done) => { // mocks testUtil.clearDb() .then(() => { @@ -76,8 +83,11 @@ describe('Project Phases', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; + projectName = p.name; // create members models.ProjectMember.bulkCreate([{ id: 1, @@ -106,7 +116,7 @@ describe('Project Phases', () => { }); }); - after((done) => { + afterEach((done) => { testUtil.clearDb(done); }); @@ -159,5 +169,49 @@ describe('Project Phases', () => { }) .end(err => expectAfterDelete(projectId, phaseId, err, done)); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when phase removed', (done) => { + request(server) + .delete(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/phases/get.spec.js b/src/routes/phases/get.spec.js index 1dab6542..8a82601d 100644 --- a/src/routes/phases/get.spec.js +++ b/src/routes/phases/get.spec.js @@ -52,6 +52,8 @@ describe('Project Phases', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phases/list.spec.js b/src/routes/phases/list.spec.js index 86b6d7e9..ce94ed25 100644 --- a/src/routes/phases/list.spec.js +++ b/src/routes/phases/list.spec.js @@ -58,6 +58,8 @@ describe('Project Phases', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; project = p.toJSON(); @@ -83,6 +85,8 @@ describe('Project Phases', () => { return models.ProjectPhase.create(body); }).then((phase) => { // Index to ES + // Overwrite lastActivityAt as otherwise ES fill not be able to parse it + project.lastActivityAt = 1; project.phases = [phase]; return server.services.es.index({ index: ES_PROJECT_INDEX, diff --git a/src/routes/phases/update.spec.js b/src/routes/phases/update.spec.js index ce9ddf16..5a3f2e86 100644 --- a/src/routes/phases/update.spec.js +++ b/src/routes/phases/update.spec.js @@ -1,10 +1,16 @@ /* eslint-disable no-unused-expressions */ import _ from 'lodash'; +import sinon from 'sinon'; import chai from 'chai'; import request from 'supertest'; import server from '../../app'; import models from '../../models'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; + +import { + BUS_API_EVENT, +} from '../../constants'; const should = chai.should(); @@ -45,6 +51,7 @@ const validatePhase = (resJson, expectedPhase) => { describe('Project Phases', () => { let projectId; + let projectName; let phaseId; let phaseId2; const memberUser = { @@ -74,8 +81,11 @@ describe('Project Phases', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { projectId = p.id; + projectName = p.name; // create members models.ProjectMember.bulkCreate([{ id: 1, @@ -261,5 +271,305 @@ describe('Project Phases', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when spentBudget updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + spentBudget: 123, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT); + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when progress updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + progress: 50, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.callCount.should.be.eql(3); + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS); + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PROGRESS_MODIFIED); + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when details updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + details: { + text: 'something', + }, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_SCOPE); + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when status updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + status: 'completed', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId, + projectName, + projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED); + done(); + }); + } + }); + }); + + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when budget updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + budget: 123, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_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 message BUS_API_EVENT.PROJECT_PLAN_UPDATED when startDate updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + startDate: 123, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_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 message BUS_API_EVENT.PROJECT_PLAN_UPDATED when duration updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + duration: 100, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_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 not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when order updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + order: 100, + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + + it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when endDate updated', (done) => { + request(server) + .patch(`/v4/projects/${projectId}/phases/${phaseId}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + endDate: new Date(), + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/projectMembers/create.spec.js b/src/routes/projectMembers/create.spec.js index 4d0c9f2f..2c3fd5ea 100644 --- a/src/routes/projectMembers/create.spec.js +++ b/src/routes/projectMembers/create.spec.js @@ -8,7 +8,8 @@ import models from '../../models'; import util from '../../util'; import server from '../../app'; import testUtil from '../../tests/util'; -import { USER_ROLE } from '../../constants'; +import busApi from '../../services/busApi'; +import { USER_ROLE, PROJECT_MEMBER_ROLE, BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -28,6 +29,8 @@ describe('Project Members create', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -49,6 +52,8 @@ describe('Project Members create', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p2) => { project2 = p2; done(); @@ -474,5 +479,146 @@ describe('Project Members create', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when manager added', (done) => { + const mockHttpClient = _.merge(testUtil.mockHttpClient, { + get: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: [{ + roleName: USER_ROLE.MANAGER, + }], + }, + }, + }), + post: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: {}, + }, + }, + }), + }); + sandbox.stub(util, 'getHttpClient', () => mockHttpClient); + request(server) + .post(`/v4/projects/${project1.id}/members/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .send({ + param: { + userId: 3, + role: PROJECT_MEMBER_ROLE.MANAGER, + }, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MEMBER_JOINED_MANAGER); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when copilot added', (done) => { + request(server) + .post(`/v4/projects/${project1.id}/members/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + userId: 3, + role: PROJECT_MEMBER_ROLE.COPILOT, + }, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MEMBER_JOINED_COPILOT); + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when customer added', (done) => { + request(server) + .post(`/v4/projects/${project1.id}/members/`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + userId: 3, + role: PROJECT_MEMBER_ROLE.CUSTOMER, + }, + }) + .expect(201) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MEMBER_JOINED); + createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/projectMembers/delete.spec.js b/src/routes/projectMembers/delete.spec.js index 3667bcff..7e7d57c7 100644 --- a/src/routes/projectMembers/delete.spec.js +++ b/src/routes/projectMembers/delete.spec.js @@ -8,6 +8,8 @@ import models from '../../models'; import util from '../../util'; import server from '../../app'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -46,6 +48,8 @@ describe('Project members delete', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -315,5 +319,87 @@ describe('Project members delete', () => { }) .expect(403, done); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when manager removed', (done) => { + const mockHttpClient = _.merge(testUtil.mockHttpClient, { + post: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: {}, + }, + }, + }), + }); + sandbox.stub(util, 'getHttpClient', () => mockHttpClient); + request(server) + .delete(`/v4/projects/${project1.id}/members/${member2.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MEMBER_LEFT); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when copilot removed', (done) => { + request(server) + .delete(`/v4/projects/${project1.id}/members/${member1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.manager}`, + }) + .expect(204) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MEMBER_REMOVED); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/projectMembers/update.spec.js b/src/routes/projectMembers/update.spec.js index 9497c416..a92144d7 100644 --- a/src/routes/projectMembers/update.spec.js +++ b/src/routes/projectMembers/update.spec.js @@ -7,6 +7,8 @@ import models from '../../models'; import server from '../../app'; import util from '../../util'; import testUtil from '../../tests/util'; +import busApi from '../../services/busApi'; +import { BUS_API_EVENT } from '../../constants'; const should = chai.should(); @@ -28,6 +30,8 @@ describe('Project members update', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -449,5 +453,50 @@ describe('Project members update', () => { } }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_TEAM_UPDATED message when user role updated', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}/members/${member2.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + role: 'customer', + }, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_TEAM_UPDATED, sinon.match({ + projectId: project1.id, + projectName: project1.name, + projectUrl: `https://local.topcoder-dev.com/projects/${project1.id}`, + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/projectUpgrade/create.spec.js b/src/routes/projectUpgrade/create.spec.js index 853f508a..1ec7859b 100644 --- a/src/routes/projectUpgrade/create.spec.js +++ b/src/routes/projectUpgrade/create.spec.js @@ -40,6 +40,8 @@ describe('Project upgrade', () => { }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, version: 'v2', directProjectId: 123, estimatedPrice: 15000, diff --git a/src/routes/projects/create.js b/src/routes/projects/create.js index b83fb4ea..d84d2ec0 100644 --- a/src/routes/projects/create.js +++ b/src/routes/projects/create.js @@ -218,6 +218,8 @@ module.exports = [ status: PROJECT_STATUS.DRAFT, createdBy: req.authUser.userId, updatedBy: req.authUser.userId, + lastActivityAt: new Date(), + lastActivityUserId: req.authUser.userId, members: [{ isPrimary: true, role: userRole, diff --git a/src/routes/projects/create.spec.js b/src/routes/projects/create.spec.js index f490d638..af442d8e 100644 --- a/src/routes/projects/create.spec.js +++ b/src/routes/projects/create.spec.js @@ -335,6 +335,9 @@ describe('Project create', () => { resJson.bookmarks.should.have.lengthOf(1); resJson.bookmarks[0].title.should.be.eql('title1'); resJson.bookmarks[0].address.should.be.eql('http://www.address.com'); + // Check that activity fields are set + resJson.lastActivityUserId.should.be.eql(40051331); + resJson.lastActivityAt.should.be.not.null; server.services.pubsub.publish.calledWith('project.draft-created').should.be.true; done(); } diff --git a/src/routes/projects/delete.spec.js b/src/routes/projects/delete.spec.js index 852d8fb4..8649edc7 100644 --- a/src/routes/projects/delete.spec.js +++ b/src/routes/projects/delete.spec.js @@ -48,6 +48,8 @@ describe('Project delete test', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members diff --git a/src/routes/projects/get.spec.js b/src/routes/projects/get.spec.js index 9fc165ee..027c8851 100644 --- a/src/routes/projects/get.spec.js +++ b/src/routes/projects/get.spec.js @@ -25,6 +25,8 @@ describe('GET Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -56,6 +58,8 @@ describe('GET Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project2 = p; }); diff --git a/src/routes/projects/list-db.js b/src/routes/projects/list-db.js index a2ce93fa..68b07761 100644 --- a/src/routes/projects/list-db.js +++ b/src/routes/projects/list-db.js @@ -102,6 +102,7 @@ module.exports = [ const sortableProps = [ 'createdAt', 'createdAt asc', 'createdAt desc', 'updatedAt', 'updatedAt asc', 'updatedAt desc', + 'lastActivityAt', 'lastActivityAt asc', 'lastActivityAt desc', 'id', 'id asc', 'id desc', 'status', 'status asc', 'status desc', 'name', 'name asc', 'name desc', diff --git a/src/routes/projects/list-db.spec.js b/src/routes/projects/list-db.spec.js index 2c9560fb..d47a4b47 100644 --- a/src/routes/projects/list-db.spec.js +++ b/src/routes/projects/list-db.spec.js @@ -1,4 +1,5 @@ /* eslint-disable no-unused-expressions */ +/* eslint-disable max-len */ import chai from 'chai'; import request from 'supertest'; @@ -61,6 +62,8 @@ describe('LIST Project db', () => { }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -101,6 +104,8 @@ describe('LIST Project db', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 2, + lastActivityUserId: 1, }).then((p) => { project2 = p; return models.ProjectMember.create({ @@ -115,12 +120,14 @@ describe('LIST Project db', () => { const p3 = models.Project.create({ type: 'visual_design', billingAccountId: 1, - name: 'test2', + name: 'test3', description: 'test project3', status: 'reviewed', details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 3, + lastActivityUserId: 1, }); return Promise.all([p1, p2, p3]) .then(() => done()); @@ -403,6 +410,72 @@ describe('LIST Project db', () => { } }); }); + + it('should return list of projects ordered ascending by lastActivityAt when sort column is "lastActivityAt"', (done) => { + request(server) + .get('/v4/projects/db/?sort=lastActivityAt') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test1'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test3'); + done(); + } + }); + }); + + it('should return list of projects ordered descending by lastActivityAt when sort column is "lastActivityAt desc"', (done) => { + request(server) + .get('/v4/projects/db/?sort=lastActivityAt desc') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test3'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test1'); + done(); + } + }); + }); + + it('should return list of projects ordered ascending by lastActivityAt when sort column is "lastActivityAt asc"', (done) => { + request(server) + .get('/v4/projects/db/?sort=lastActivityAt asc') + .set({ + Authorization: `Bearer ${testUtil.jwts.connectAdmin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test1'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test3'); + done(); + } + }); + }); }); }); }); diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index 483cff4d..c890619d 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -285,6 +285,7 @@ module.exports = [ 'best match', 'createdAt', 'createdAt asc', 'createdAt desc', 'updatedAt', 'updatedAt asc', 'updatedAt desc', + 'lastActivityAt', 'lastActivityAt asc', 'lastActivityAt desc', 'id', 'id asc', 'id desc', 'status', 'status asc', 'status desc', 'name', 'name asc', 'name desc', diff --git a/src/routes/projects/list.spec.js b/src/routes/projects/list.spec.js index b69fcf6f..02ca46f9 100644 --- a/src/routes/projects/list.spec.js +++ b/src/routes/projects/list.spec.js @@ -1,4 +1,5 @@ /* eslint-disable no-unused-expressions */ +/* eslint-disable max-len */ import chai from 'chai'; import request from 'supertest'; import sleep from 'sleep'; @@ -28,6 +29,8 @@ const data = [ }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, members: [ { id: 1, @@ -72,6 +75,8 @@ const data = [ details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 2, + lastActivityUserId: 1, members: [ { id: 1, @@ -88,12 +93,14 @@ const data = [ id: 3, type: 'visual_design', billingAccountId: 1, - name: 'test2', + name: 'test3', description: 'test project3', status: 'reviewed', details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 3, + lastActivityUserId: 1, }, ]; @@ -118,6 +125,8 @@ describe('LIST Project', () => { }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }).then((p) => { project1 = p; // create members @@ -158,6 +167,8 @@ describe('LIST Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 2, + lastActivityUserId: 1, }).then((p) => { project2 = p; return models.ProjectMember.create({ @@ -172,12 +183,14 @@ describe('LIST Project', () => { const p3 = models.Project.create({ type: 'visual_design', billingAccountId: 1, - name: 'test2', + name: 'test3', description: 'test project3', status: 'reviewed', details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 3, + lastActivityUserId: 1, }).then((p) => { project3 = p; return Promise.resolve(); @@ -474,6 +487,72 @@ describe('LIST Project', () => { }); }); + it('should return list of projects ordered ascending by lastActivityAt when sort column is "lastActivityAt"', (done) => { + request(server) + .get('/v4/projects/?sort=lastActivityAt') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test1'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test3'); + done(); + } + }); + }); + + it('should return list of projects ordered descending by lastActivityAt when sort column is "lastActivityAt desc"', (done) => { + request(server) + .get('/v4/projects/?sort=lastActivityAt desc') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test3'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test1'); + done(); + } + }); + }); + + it('should return list of projects ordered ascending by lastActivityAt when sort column is "lastActivityAt asc"', (done) => { + request(server) + .get('/v4/projects/?sort=lastActivityAt asc') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + resJson.should.have.lengthOf(3); + resJson[0].name.should.equal('test1'); + resJson[1].name.should.equal('test2'); + resJson[2].name.should.equal('test3'); + done(); + } + }); + }); + describe('GET All /projects/ for Connect Admin, ', () => { it('should return the project ', (done) => { request(server) diff --git a/src/routes/projects/update.spec.js b/src/routes/projects/update.spec.js index 42abf4f0..e76cb0af 100644 --- a/src/routes/projects/update.spec.js +++ b/src/routes/projects/update.spec.js @@ -8,8 +8,12 @@ import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; import util from '../../util'; + +import busApi from '../../services/busApi'; + import { PROJECT_STATUS, + BUS_API_EVENT, } from '../../constants'; const should = chai.should(); @@ -62,6 +66,8 @@ describe('Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }, { @@ -73,6 +79,8 @@ describe('Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }, { @@ -83,6 +91,8 @@ describe('Project', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }]) @@ -809,5 +819,254 @@ describe('Project', () => { }); }); }); + + describe('Bus api', () => { + let createEventSpy; + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + it('sends single BUS_API_EVENT.PROJECT_UPDATED message on project status update', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + status: PROJECT_STATUS.COMPLETED, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_COMPLETED); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_UPDATED message on project details update', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + details: { + info: 'something', + }, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_SPECIFICATION_MODIFIED); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_UPDATED message on project name update', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + name: 'New project name', + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_SPECIFICATION_MODIFIED); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_UPDATED message on project description update', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + description: 'Updated description', + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('sends single BUS_API_EVENT.PROJECT_UPDATED message on project bookmarks update', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + bookmarks: [{ + title: 'title1', + address: 'http://someurl.com', + }], + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledTwice.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_SPECIFICATION_MODIFIED); + createEventSpy.secondCall.calledWith(BUS_API_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(); + }); + } + }); + }); + + it('should not send BUS_API_EVENT.PROJECT_UPDATED message when project estimatedPrice is updated', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + estimatedPrice: 123, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + + it('should not send BUS_API_EVENT.PROJECT_UPDATED message when project actualPrice is updated', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + actualPrice: 123, + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + + it('should not send BUS_API_EVENT.PROJECT_UPDATED message when project terms are updated', (done) => { + request(server) + .patch(`/v4/projects/${project1.id}`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send({ + param: { + terms: [1, 2, 3], + }, + }) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.notCalled.should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/routes/timelines/create.spec.js b/src/routes/timelines/create.spec.js index 7ce3e9c3..2c8bfcde 100644 --- a/src/routes/timelines/create.spec.js +++ b/src/routes/timelines/create.spec.js @@ -22,6 +22,8 @@ const testProjects = [ details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -33,6 +35,8 @@ const testProjects = [ createdBy: 2, updatedBy: 2, deletedAt: '2018-05-15T00:00:00Z', + lastActivityAt: 1, + lastActivityUserId: 1, }, ]; diff --git a/src/routes/timelines/delete.spec.js b/src/routes/timelines/delete.spec.js index 44ad6f07..4b0b76f3 100644 --- a/src/routes/timelines/delete.spec.js +++ b/src/routes/timelines/delete.spec.js @@ -51,6 +51,8 @@ describe('DELETE timeline', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -61,6 +63,8 @@ describe('DELETE timeline', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/get.spec.js b/src/routes/timelines/get.spec.js index 253013da..39eb4bc6 100644 --- a/src/routes/timelines/get.spec.js +++ b/src/routes/timelines/get.spec.js @@ -72,6 +72,8 @@ describe('GET timeline', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -82,6 +84,8 @@ describe('GET timeline', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/list.spec.js b/src/routes/timelines/list.spec.js index 877141de..d8cda262 100644 --- a/src/routes/timelines/list.spec.js +++ b/src/routes/timelines/list.spec.js @@ -112,6 +112,8 @@ describe('LIST timelines', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -122,6 +124,8 @@ describe('LIST timelines', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/update.js b/src/routes/timelines/update.js index e91783e4..bf403148 100644 --- a/src/routes/timelines/update.js +++ b/src/routes/timelines/update.js @@ -104,6 +104,8 @@ module.exports = [ { original, updated }, { correlationId: req.id }, ); + req.app.emit(EVENT.ROUTING_KEY.TIMELINE_UPDATED, + { req, original, updated }); // Write to response res.json(util.wrapResponse(req.id, updated)); diff --git a/src/routes/timelines/update.spec.js b/src/routes/timelines/update.spec.js index cf00398c..19eecd6f 100644 --- a/src/routes/timelines/update.spec.js +++ b/src/routes/timelines/update.spec.js @@ -1,17 +1,19 @@ +/* eslint-disable no-unused-expressions */ /** * Tests for get.js */ import chai from 'chai'; +import sinon from 'sinon'; import request from 'supertest'; import _ from 'lodash'; import models from '../../models'; import server from '../../app'; import testUtil from '../../tests/util'; -import { EVENT } from '../../constants'; +import { EVENT, BUS_API_EVENT } from '../../constants'; +import busApi from '../../services/busApi'; const should = chai.should(); - const milestones = [ { id: 1, @@ -74,6 +76,8 @@ describe('UPDATE timeline', () => { details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', @@ -84,6 +88,8 @@ describe('UPDATE timeline', () => { details: {}, createdBy: 2, updatedBy: 2, + lastActivityAt: 1, + lastActivityUserId: 1, deletedAt: '2018-05-15T00:00:00Z', }, ], { returning: true }) @@ -623,5 +629,52 @@ describe('UPDATE timeline', () => { .expect(200) .end(done); }); + + describe('Bus api', () => { + let createEventSpy; + const sandbox = sinon.sandbox.create(); + + before((done) => { + // Wait for 500ms in order to wait for createEvent calls from previous tests to complete + testUtil.wait(done); + }); + + beforeEach(() => { + createEventSpy = sandbox.spy(busApi, 'createEvent'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + // not testing fields separately as startDate is required parameter, + // thus PROJECT_PLAN_UPDATED will be always sent + it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when timeline updated', (done) => { + request(server) + .patch('/v4/timelines/1') + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send(body) + .expect(200) + .end((err) => { + if (err) { + done(err); + } else { + testUtil.wait(() => { + createEventSpy.calledOnce.should.be.true; + createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; + done(); + }); + } + }); + }); + }); }); }); diff --git a/src/services/index.js b/src/services/index.js index a1d84c12..a6bbd776 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,6 +2,8 @@ import config from 'config'; import RabbitMQService from './rabbitmq'; +import startKafkaConsumer from './kafka/kafkaConsumer'; +import kafkaHandler from './kafka/kafkaHandlers'; /** * Responsible for establishing connections to all external services @@ -31,6 +33,10 @@ module.exports = (fapp, logger) => { .then(() => { logger.info('RabbitMQ service initialized'); }) + .then(() => startKafkaConsumer(kafkaHandler, app, logger)) + .then(() => { + logger.info('Kafka consumer service initialized'); + }) .catch((err) => { logger.error('Error initializing services', err); // gracefulShutdown() diff --git a/src/services/kafka/kafkaConsumer.js b/src/services/kafka/kafkaConsumer.js new file mode 100644 index 00000000..4b74f485 --- /dev/null +++ b/src/services/kafka/kafkaConsumer.js @@ -0,0 +1,57 @@ +import Kafka from 'no-kafka'; +import config from 'config'; +/** + * Initializes Kafka consumer and subscribes for the topics + * @param {Object} handlers Object that holds kafka handlers. Where property name is kafka topic and value is handler + * @param {Object} app Application object + * @param {Object} logger Logger object + * @return {Promise} Promise that got resolved on successful consumer creation + */ +export default async function startKafkaConsumer(handlers, app, logger) { + // Read config and prepare Kafka options object + const kafkaConfig = config.get('kafkaConfig'); + + const options = {}; + if (kafkaConfig.has('hosts')) { + options.connectionString = kafkaConfig.get('hosts'); + } + if (kafkaConfig.has('clientCert') && kafkaConfig.has('clientCertKey')) { + options.ssl = { + cert: kafkaConfig.get('clientCert'), + key: kafkaConfig.has('clientCertKey'), + }; + } + + const consumer = new Kafka.SimpleConsumer(options); + await consumer.init(); + + /** + * Function is invoked each time new messages are written to specified topic. + * Calls handler for each message in messageSet. Outputs errors from handler into logger without + * interrupting the application. + * @param {Array} messageSet list of received messages + * @param {String} topic topic where messages are written + * @param {Number} partition partition where messages are written + * @return {Promise} Promise + */ + const onConsume = async (messageSet, topic, partition) => { + messageSet.forEach(async (kafkaMessage) => { + try { + const handler = handlers[topic]; + if (!handler) { + logger.info(`No handler configured for topic: ${topic}`); + return; + } + + await handler(app, topic, kafkaMessage.message.value.toString('utf8')); + await consumer.commitOffset({ topic, partition, offset: kafkaMessage.offset }); + } catch (error) { + logger.error(`Message processing failed: ${error}`); + } + }); + }; + + // Subscribe for all topics defined in handlers + const promises = Object.keys(handlers).map(topic => consumer.subscribe(topic, onConsume)); + await Promise.all(promises); +} diff --git a/src/services/kafka/kafkaConsumer.spec.js b/src/services/kafka/kafkaConsumer.spec.js new file mode 100644 index 00000000..0be86d34 --- /dev/null +++ b/src/services/kafka/kafkaConsumer.spec.js @@ -0,0 +1,108 @@ +/* eslint-disable no-unused-expressions */ +import sinon from 'sinon'; +import * as Kafka from 'no-kafka'; + +import startKafkaConsumer from './kafkaConsumer'; + +describe('Kafka service', () => { + const sandbox = sinon.sandbox.create(); + let mockedSubscribe; + + const mockedLogger = { + debug: sinon.stub(), + info: sinon.stub(), + error: sinon.stub(), + }; + + const mockedApp = { + services: { + pubsub: { + publish: sinon.stub(), + }, + }, + }; + + // Mock Kafka consumer class + before(() => { + mockedSubscribe = sinon.stub(Kafka.SimpleConsumer.prototype, 'subscribe'); + sinon.stub(Kafka.SimpleConsumer.prototype, 'init').returns(Promise.resolve()); + sinon.stub(Kafka.SimpleConsumer.prototype, 'commitOffset').returns(Promise.resolve()); + }); + + afterEach(() => { + sandbox.reset(); + }); + + after(() => { + sandbox.restore(); + }); + + it('subscribes to every topic defined in handlers', async () => { + const handlers = { + topic1: () => {}, + topic2: () => {}, + }; + + await startKafkaConsumer(handlers, mockedApp, mockedLogger); + + mockedSubscribe.calledTwice.should.be.true; + mockedSubscribe.firstCall.calledWith('topic1').should.be.true; + mockedSubscribe.secondCall.calledWith('topic2').should.be.true; + }); + + describe('consumer', () => { + let consumerFunction; + + let handlers; + + beforeEach(async () => { + mockedLogger.error.reset(); + + handlers = { + topic1: sinon.stub(), + topic2: sinon.stub(), + }; + await startKafkaConsumer(handlers, mockedApp, mockedLogger); + // Get consumer function + consumerFunction = mockedSubscribe.lastCall.args[1]; + }); + + it('calls handler for specific topic only', async () => { + await consumerFunction([{ + message: { + value: 'message', + }, + }], 'topic1', {}); + + handlers.topic1.calledOnce.should.be.true; + handlers.topic2.notCalled.should.be.true; + }); + + it('logs error and continues when handler fails', async () => { + handlers.topic2 = sinon.stub().returns(Promise.reject('failure')); + + await consumerFunction([{ + message: { + value: 'message', + }, + }], 'topic2', {}); + + handlers.topic2.calledOnce.should.be.true; + mockedLogger.error.calledOnce.should.be.true; + mockedLogger.error.calledWith('Message processing failed: failure'); + }); + + it('drops message when handler not found', async () => { + await consumerFunction([{ + message: { + value: 'message', + }, + }], 'unknown-topic', {}); + + handlers.topic1.notCalled.should.be.true; + handlers.topic2.notCalled.should.be.true; + mockedLogger.info.calledOnce.should.be.true; + mockedLogger.info.calledWith('No handler configured for topic: unknown-topic').should.be.true; + }); + }); +}); diff --git a/src/services/kafka/kafkaHandlers.js b/src/services/kafka/kafkaHandlers.js new file mode 100644 index 00000000..92d411d0 --- /dev/null +++ b/src/services/kafka/kafkaHandlers.js @@ -0,0 +1,16 @@ +import updateProjectActivity from './updateProjectActivity'; +import { BUS_API_EVENT } from '../../constants'; + +// Mapping of Kafka topic and handler +export default { + // Events defined by project-service + [BUS_API_EVENT.PROJECT_UPDATED]: updateProjectActivity, + [BUS_API_EVENT.PROJECT_FILES_UPDATED]: updateProjectActivity, + [BUS_API_EVENT.PROJECT_TEAM_UPDATED]: updateProjectActivity, + [BUS_API_EVENT.PROJECT_PLAN_UPDATED]: updateProjectActivity, + // Events from message-service + 'notifications.connect.project.topic.created': updateProjectActivity, + 'notifications.connect.project.topic.updated': updateProjectActivity, + 'notifications.connect.project.post.created': updateProjectActivity, + 'notifications.connect.project.post.edited': updateProjectActivity, +}; diff --git a/src/services/kafka/updateProjectActivity.js b/src/services/kafka/updateProjectActivity.js new file mode 100644 index 00000000..c1761953 --- /dev/null +++ b/src/services/kafka/updateProjectActivity.js @@ -0,0 +1,49 @@ +import Joi from 'joi'; +import uuid from 'uuid'; +import models from '../../models'; +import { REGEX, EVENT } from '../../constants'; + +const payloadSchema = Joi.object().keys({ + projectId: Joi.number().integer().positive().required(), + projectName: Joi.string().required(), + projectUrl: Joi.string().regex(REGEX.URL).required(), + userId: Joi.number().integer().positive().required(), + initiatorUserId: Joi.number().integer().positive().required(), +}).required(); + +/** + * Updates project activity fields. throws exceptions in case of error + * @param {Object} app Application object used to interact with RMQ service + * @param {String} topic Kafka topic + * @param {Object} payload Message payload + * @return {Promise} Promise + */ +export default async function updateProjectActivity(app, topic, payload) { + // Validate payload + const jsonMessage = JSON.parse(payload); + const result = Joi.validate(jsonMessage, payloadSchema); + if (result.error) { + throw new Error(result.error); + } + + // Find project by id and update activity. Single update is used as there is no need to wrap it into transaction + const projectId = jsonMessage.projectId; + const project = await models.Project.findById(projectId); + if (!project) { + throw new Error(`Project with id ${projectId} not found`); + } + const previousValue = project.get({ plain: true }); + project.lastActivityAt = new Date(); + project.lastActivityUserId = jsonMessage.initiatorUserId; + + await project.save(); + + app.services.pubsub.publish( + EVENT.ROUTING_KEY.PROJECT_UPDATED, { + original: previousValue, + updated: project.get({ plain: true }), + }, { + correlationId: uuid.v4(), + }, + ); +} diff --git a/src/services/kafka/updateProjectActivity.spec.js b/src/services/kafka/updateProjectActivity.spec.js new file mode 100644 index 00000000..b52cb6e2 --- /dev/null +++ b/src/services/kafka/updateProjectActivity.spec.js @@ -0,0 +1,132 @@ +/* eslint-disable no-unused-expressions */ +import _ from 'lodash'; +import sinon from 'sinon'; +import chai, { expect } from 'chai'; +import models from '../../models'; +import updateProjectActivity from './updateProjectActivity'; +import testUtil from '../../tests/util'; + +chai.use(require('chai-as-promised')); + +describe('updateProjectActivity', () => { + // Any topic name is fine here as routing happens in kafkaConsumer + const topic = 'topic'; + + const validPayload = { + projectId: 1, + projectName: 'test project', + projectUrl: 'http://someurl.com', + userId: 1, + initiatorUserId: 2, + }; + + const mockedApp = { + services: { + pubsub: { + publish: sinon.stub(), + }, + }, + }; + + it('should throw exception when payload is not a valid json', async () => { + await expect(updateProjectActivity(mockedApp, topic, 'string')).to.be.rejectedWith(SyntaxError); + }); + + it('should throw validation exception when payload is empty', async () => { + await expect(updateProjectActivity(mockedApp, topic, '{}')).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when projectId is not set', async () => { + const payload = _.omit(validPayload, 'projectId'); + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when projectName is not set', async () => { + const payload = _.omit(validPayload, 'projectName'); + await expect(updateProjectActivity(mockedApp, mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when projectUrl is not set', async () => { + const payload = _.omit(validPayload, 'projectUrl'); + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when userId is not set', async () => { + const payload = _.omit(validPayload, 'userId'); + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when initiatorUserId is not set', async () => { + const payload = _.omit(validPayload, 'initiatorUserId'); + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when projectId is not integer', async () => { + const payload = _.clone(validPayload); + payload.projectId = 'string'; + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when projectUrl is not a valid url', async () => { + const payload = _.clone(validPayload); + payload.projectUrl = 'string'; + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when userId is not integer', async () => { + const payload = _.clone(validPayload); + payload.userId = 'string'; + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + it('should throw validation exception when initiatorUserId is not integer', async () => { + const payload = _.clone(validPayload); + payload.initiatorUserId = 'string'; + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + }); + + describe('integration', () => { + let project; + + beforeEach(async () => { + await testUtil.clearDb(); + project = await models.Project.create({ + type: 'generic', + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, + }); + }); + + afterEach(async () => { + await testUtil.clearDb(); + }); + + it('should throw exception when project not found by id', async () => { + const payload = _.clone(validPayload); + payload.projectId = 2; + await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be + .rejectedWith(Error, 'Project with id 2 not found'); + }); + + it('should update lastActivityAt and lastActivityUserId columns in db', async () => { + await updateProjectActivity(mockedApp, topic, JSON.stringify(validPayload)); + + const updatedProject = await models.Project.findById(project.id); + expect(updatedProject.lastActivityUserId).to.be.eql(2); + expect(updatedProject.lastActivityAt).to.be.greaterThan(project.lastActivityAt); + }); + + it('should update ES index', async () => { + await updateProjectActivity(mockedApp, topic, JSON.stringify(validPayload)); + // Check that message has been sent to RabbitMQ as it updates ES + expect(mockedApp.services.pubsub.publish).to.be.called; + }); + }); +}); diff --git a/src/tests/seed.js b/src/tests/seed.js index 6b693e5c..d184faa2 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -13,6 +13,8 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'visual_design', directProjectId: 1, @@ -23,6 +25,8 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'visual_design', billingAccountId: 3, @@ -32,6 +36,8 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', billingAccountId: 4, @@ -41,6 +47,8 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', billingAccountId: 5, @@ -50,6 +58,8 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, }, { type: 'generic', billingAccountId: 5, @@ -69,6 +79,8 @@ models.sequelize.sync({ force: true }) }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, version: 'v2', directProjectId: 123, estimatedPrice: 15000, @@ -92,6 +104,8 @@ models.sequelize.sync({ force: true }) }, createdBy: 1, updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: 1, version: 'v2', directProjectId: 123, estimatedPrice: 15000, diff --git a/src/tests/util.js b/src/tests/util.js index f3dff595..232c553d 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -28,4 +28,7 @@ export default { connectAdmin: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJDb25uZWN0IEFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJjb25uZWN0X2FkbWluMSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzYiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoiY29ubmVjdF9hZG1pbjFAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.nSGfXMl02NZ90ZKLiEKPg75iAjU92mfteaY6xgqkM30', }, getDecodedToken: token => jwt.decode(token), + + // Waits for 500ms and executes cb function + wait: cb => setTimeout(cb, 500), }; From 004efd669d71ed18eb8404da4a0c072f22506c28 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 14:07:08 +0800 Subject: [PATCH 41/77] make git ignore `.env` files which is useful to use for local config so they are not accidentally committed --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0cf4a82a..edd85b28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Created by https://www.gitignore.io/api/node config/local.js +# can be used locally to config some env variables and after apply them using `source .env` +.env ### Node ### # Logs logs From 8fb395f0ab186f9cd01cb980ff87e9dc1ea25b66 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 17:09:51 +0800 Subject: [PATCH 42/77] added lastActivityAt and lastActivityUserId to ES project index --- migrations/elasticsearch_sync.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migrations/elasticsearch_sync.js b/migrations/elasticsearch_sync.js index eac45e5f..ef568f19 100644 --- a/migrations/elasticsearch_sync.js +++ b/migrations/elasticsearch_sync.js @@ -281,6 +281,13 @@ function getRequestBody(indexName) { updatedBy: { type: 'integer', }, + lastActivityAt: { + type: 'date', + format: 'strict_date_optional_time||epoch_millis', + }, + lastActivityUserId: { + type: 'integer', + }, utm: { properties: { campaign: { From 10f0093ff848514a86bc35c8ce691be0c7009a32 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 17:11:58 +0800 Subject: [PATCH 43/77] fixed migration script for project activity so lastActivityAt has time zone --- migrations/20180910_project_activity.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/20180910_project_activity.sql b/migrations/20180910_project_activity.sql index 81fbc727..8c84b34b 100644 --- a/migrations/20180910_project_activity.sql +++ b/migrations/20180910_project_activity.sql @@ -8,7 +8,7 @@ -- projects -- Add new columns -ALTER TABLE projects ADD COLUMN "lastActivityAt" timestamp; +ALTER TABLE projects ADD COLUMN "lastActivityAt" timestamp with time zone; ALTER TABLE projects ADD COLUMN "lastActivityUserId" INT; -- Update new colums UPDATE projects SET "lastActivityAt"="updatedAt" WHERE "lastActivityAt" is NULL; From 344098b7878a0edf2574bada19e38baecbf68ea2 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 19:39:46 +0800 Subject: [PATCH 44/77] Update seed to have the today's date for `lastActivityAt` instead of 1 (the beginning of Unix time) --- src/tests/seed.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tests/seed.js b/src/tests/seed.js index d184faa2..a98e82f9 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -13,7 +13,7 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, }, { type: 'visual_design', @@ -25,7 +25,7 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, }, { type: 'visual_design', @@ -36,7 +36,7 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, }, { type: 'generic', @@ -47,7 +47,7 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, }, { type: 'generic', @@ -58,7 +58,7 @@ models.sequelize.sync({ force: true }) details: {}, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, }, { type: 'generic', @@ -79,7 +79,7 @@ models.sequelize.sync({ force: true }) }, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, version: 'v2', directProjectId: 123, @@ -104,7 +104,7 @@ models.sequelize.sync({ force: true }) }, createdBy: 1, updatedBy: 1, - lastActivityAt: 1, + lastActivityAt: new Date(), lastActivityUserId: 1, version: 'v2', directProjectId: 123, From 931b0e74c79bbe48ee48d3481ca408342787ea92 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 20:14:58 +0800 Subject: [PATCH 45/77] Create and use constants for message service events instead of hardcode them --- src/constants.js | 6 ++++++ src/services/kafka/kafkaHandlers.js | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/constants.js b/src/constants.js index c9af6d95..c41703e8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -112,6 +112,12 @@ export const BUS_API_EVENT = { MILESTONE_TRANSITION_COMPLETED: 'notifications.connect.project.phase.milestone.transition.completed', // When milestone is waiting for customers's input MILESTONE_WAITING_CUSTOMER: 'notifications.connect.project.phase.milestone.waiting.customer', + + // TC Message Service events + TOPIC_CREATED: 'notifications.connect.project.topic.created', + TOPIC_UPDATED: 'notifications.connect.project.topic.updated', + POST_CREATED: 'notifications.connect.project.post.created', + POST_UPDATED: 'notifications.connect.project.post.edited', }; export const REGEX = { diff --git a/src/services/kafka/kafkaHandlers.js b/src/services/kafka/kafkaHandlers.js index 92d411d0..728cbe8c 100644 --- a/src/services/kafka/kafkaHandlers.js +++ b/src/services/kafka/kafkaHandlers.js @@ -9,8 +9,8 @@ export default { [BUS_API_EVENT.PROJECT_TEAM_UPDATED]: updateProjectActivity, [BUS_API_EVENT.PROJECT_PLAN_UPDATED]: updateProjectActivity, // Events from message-service - 'notifications.connect.project.topic.created': updateProjectActivity, - 'notifications.connect.project.topic.updated': updateProjectActivity, - 'notifications.connect.project.post.created': updateProjectActivity, - 'notifications.connect.project.post.edited': updateProjectActivity, + [BUS_API_EVENT.TOPIC_CREATED]: updateProjectActivity, + [BUS_API_EVENT.TOPIC_UPDATED]: updateProjectActivity, + [BUS_API_EVENT.POST_CREATED]: updateProjectActivity, + [BUS_API_EVENT.POST_UPDATED]: updateProjectActivity, }; From 4874fb38d7df0da7bc7891e0f61a496458ce9b67 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Sep 2018 21:19:35 +0800 Subject: [PATCH 46/77] call ES directly after updating project activity instead of using RabbitMQ event --- package.json | 3 +-- src/services/kafka/kafkaConsumer.js | 1 + src/services/kafka/updateProjectActivity.js | 28 ++++++++++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 0eda36d5..5257963c 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,7 @@ "sequelize": "^3.23.0", "tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.3", "traverse": "^0.6.6", - "urlencode": "^1.1.0", - "uuid": "^3.3.2" + "urlencode": "^1.1.0" }, "devDependencies": { "babel-cli": "^6.9.0", diff --git a/src/services/kafka/kafkaConsumer.js b/src/services/kafka/kafkaConsumer.js index 4b74f485..4d9aa895 100644 --- a/src/services/kafka/kafkaConsumer.js +++ b/src/services/kafka/kafkaConsumer.js @@ -45,6 +45,7 @@ export default async function startKafkaConsumer(handlers, app, logger) { await handler(app, topic, kafkaMessage.message.value.toString('utf8')); await consumer.commitOffset({ topic, partition, offset: kafkaMessage.offset }); + logger.info(`Message for topic '${topic}' was successfully processed`); } catch (error) { logger.error(`Message processing failed: ${error}`); } diff --git a/src/services/kafka/updateProjectActivity.js b/src/services/kafka/updateProjectActivity.js index c1761953..a6bd44da 100644 --- a/src/services/kafka/updateProjectActivity.js +++ b/src/services/kafka/updateProjectActivity.js @@ -1,7 +1,13 @@ +import _ from 'lodash'; import Joi from 'joi'; -import uuid from 'uuid'; +import config from 'config'; +import util from '../../util'; import models from '../../models'; -import { REGEX, EVENT } from '../../constants'; +import { REGEX } from '../../constants'; + +const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); +const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); +const eClient = util.getElasticSearchClient(); const payloadSchema = Joi.object().keys({ projectId: Joi.number().integer().positive().required(), @@ -38,12 +44,16 @@ export default async function updateProjectActivity(app, topic, payload) { await project.save(); - app.services.pubsub.publish( - EVENT.ROUTING_KEY.PROJECT_UPDATED, { - original: previousValue, - updated: project.get({ plain: true }), - }, { - correlationId: uuid.v4(), + // first get the existing document and than merge the updated changes and save the new document + const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); + const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle + // update the merged document + await eClient.update({ + index: ES_PROJECT_INDEX, + type: ES_PROJECT_TYPE, + id: previousValue.id, + body: { + doc: merged, }, - ); + }); } From 2eb91595380975a59b678088255c42763078161a Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Tue, 18 Sep 2018 11:19:06 +0800 Subject: [PATCH 47/77] fix Kafka consumer to get messages in proper format --- src/services/kafka/kafkaConsumer.js | 5 ++++- src/services/kafka/updateProjectActivity.js | 7 +++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/services/kafka/kafkaConsumer.js b/src/services/kafka/kafkaConsumer.js index 4d9aa895..639deced 100644 --- a/src/services/kafka/kafkaConsumer.js +++ b/src/services/kafka/kafkaConsumer.js @@ -36,6 +36,7 @@ export default async function startKafkaConsumer(handlers, app, logger) { */ const onConsume = async (messageSet, topic, partition) => { messageSet.forEach(async (kafkaMessage) => { + logger.debug(`Consume topic '${topic}' with message: '${kafkaMessage.message.value.toString('utf8')}'.`); try { const handler = handlers[topic]; if (!handler) { @@ -43,7 +44,9 @@ export default async function startKafkaConsumer(handlers, app, logger) { return; } - await handler(app, topic, kafkaMessage.message.value.toString('utf8')); + const busMessage = JSON.parse(kafkaMessage.message.value.toString('utf8')); + const payload = busMessage.payload; + await handler(app, topic, payload); await consumer.commitOffset({ topic, partition, offset: kafkaMessage.offset }); logger.info(`Message for topic '${topic}' was successfully processed`); } catch (error) { diff --git a/src/services/kafka/updateProjectActivity.js b/src/services/kafka/updateProjectActivity.js index a6bd44da..398433fd 100644 --- a/src/services/kafka/updateProjectActivity.js +++ b/src/services/kafka/updateProjectActivity.js @@ -26,21 +26,20 @@ const payloadSchema = Joi.object().keys({ */ export default async function updateProjectActivity(app, topic, payload) { // Validate payload - const jsonMessage = JSON.parse(payload); - const result = Joi.validate(jsonMessage, payloadSchema); + const result = Joi.validate(payload, payloadSchema); if (result.error) { throw new Error(result.error); } // Find project by id and update activity. Single update is used as there is no need to wrap it into transaction - const projectId = jsonMessage.projectId; + const projectId = payload.projectId; const project = await models.Project.findById(projectId); if (!project) { throw new Error(`Project with id ${projectId} not found`); } const previousValue = project.get({ plain: true }); project.lastActivityAt = new Date(); - project.lastActivityUserId = jsonMessage.initiatorUserId; + project.lastActivityUserId = payload.initiatorUserId; await project.save(); From 8f4929a5953e9fbd59dc7c6272e13bfc4716b052 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Tue, 18 Sep 2018 12:04:14 +0800 Subject: [PATCH 48/77] Use string for lastActivityUserId instead of integers --- migrations/20180910_project_activity.sql | 4 ++-- migrations/elasticsearch_sync.js | 2 +- src/models/project.js | 4 +++- src/routes/admin/project-create-index.js | 2 +- src/tests/seed.js | 14 +++++++------- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/migrations/20180910_project_activity.sql b/migrations/20180910_project_activity.sql index 8c84b34b..540d5491 100644 --- a/migrations/20180910_project_activity.sql +++ b/migrations/20180910_project_activity.sql @@ -9,10 +9,10 @@ -- Add new columns ALTER TABLE projects ADD COLUMN "lastActivityAt" timestamp with time zone; -ALTER TABLE projects ADD COLUMN "lastActivityUserId" INT; +ALTER TABLE projects ADD COLUMN "lastActivityUserId" character varying(45); -- Update new colums UPDATE projects SET "lastActivityAt"="updatedAt" WHERE "lastActivityAt" is NULL; -UPDATE projects SET "lastActivityUserId"="updatedBy" WHERE "lastActivityUserId" is NULL; +UPDATE projects SET "lastActivityUserId"=cast("updatedBy" as varchar) WHERE "lastActivityUserId" is NULL; -- Set not null ALTER TABLE projects ALTER COLUMN "lastActivityAt" SET NOT NULL; ALTER TABLE projects ALTER COLUMN "lastActivityUserId" SET NOT NULL; diff --git a/migrations/elasticsearch_sync.js b/migrations/elasticsearch_sync.js index ef568f19..349be0b6 100644 --- a/migrations/elasticsearch_sync.js +++ b/migrations/elasticsearch_sync.js @@ -286,7 +286,7 @@ function getRequestBody(indexName) { format: 'strict_date_optional_time||epoch_millis', }, lastActivityUserId: { - type: 'integer', + type: 'string', }, utm: { properties: { diff --git a/src/models/project.js b/src/models/project.js index f7d52128..7fdf81d0 100644 --- a/src/models/project.js +++ b/src/models/project.js @@ -43,7 +43,9 @@ module.exports = function defineProject(sequelize, DataTypes) { updatedBy: { type: DataTypes.INTEGER, allowNull: false }, version: { type: DataTypes.STRING(3), allowNull: false, defaultValue: 'v3' }, lastActivityAt: { type: DataTypes.DATE, allowNull: false }, - lastActivityUserId: { type: DataTypes.INTEGER, allowNull: false }, + // we use string for `lastActivityUserId` because it comes in Kafka messages payloads + // and can be not only user id but also `coderbot`, `system` or some kind of autopilot bot id in the future + lastActivityUserId: { type: DataTypes.STRING, allowNull: false }, }, { tableName: 'projects', paranoid: true, diff --git a/src/routes/admin/project-create-index.js b/src/routes/admin/project-create-index.js index 6cc27247..d23738ea 100644 --- a/src/routes/admin/project-create-index.js +++ b/src/routes/admin/project-create-index.js @@ -258,7 +258,7 @@ function getRequestBody(indexName, docType) { format: 'strict_date_optional_time||epoch_millis', }, lastActivityUserId: { - type: 'long', + type: 'string', }, userId: { type: 'long', diff --git a/src/tests/seed.js b/src/tests/seed.js index a98e82f9..5c4f553d 100644 --- a/src/tests/seed.js +++ b/src/tests/seed.js @@ -14,7 +14,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'visual_design', directProjectId: 1, @@ -26,7 +26,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'visual_design', billingAccountId: 3, @@ -37,7 +37,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', billingAccountId: 4, @@ -48,7 +48,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', billingAccountId: 5, @@ -59,7 +59,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', billingAccountId: 5, @@ -80,7 +80,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', version: 'v2', directProjectId: 123, estimatedPrice: 15000, @@ -105,7 +105,7 @@ models.sequelize.sync({ force: true }) createdBy: 1, updatedBy: 1, lastActivityAt: new Date(), - lastActivityUserId: 1, + lastActivityUserId: '1', version: 'v2', directProjectId: 123, estimatedPrice: 15000, From 9ff7e4b3f3ee8e9305d3dc15580d4ce07e7f4044 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Tue, 18 Sep 2018 17:47:38 +0800 Subject: [PATCH 49/77] move kafka event handlers from service folder to events folder --- src/events/index.js | 21 ++++++- src/events/projects/index.js | 56 ++++++++++++++++++ .../projects/index.spec.js} | 33 ++++++----- src/services/index.js | 6 +- src/services/kafka/kafkaHandlers.js | 16 ----- src/services/kafka/updateProjectActivity.js | 58 ------------------- src/services/{kafka => }/kafkaConsumer.js | 0 .../{kafka => }/kafkaConsumer.spec.js | 0 src/services/rabbitmq.js | 2 +- 9 files changed, 95 insertions(+), 97 deletions(-) rename src/{services/kafka/updateProjectActivity.spec.js => events/projects/index.spec.js} (67%) delete mode 100644 src/services/kafka/kafkaHandlers.js delete mode 100644 src/services/kafka/updateProjectActivity.js rename src/services/{kafka => }/kafkaConsumer.js (100%) rename src/services/{kafka => }/kafkaConsumer.spec.js (100%) diff --git a/src/events/index.js b/src/events/index.js index fac17d8d..38a1ed00 100644 --- a/src/events/index.js +++ b/src/events/index.js @@ -1,6 +1,7 @@ -import { EVENT } from '../constants'; -import { projectCreatedHandler, projectUpdatedHandler, projectDeletedHandler } from './projects'; +import { EVENT, BUS_API_EVENT } from '../constants'; +import { projectCreatedHandler, projectUpdatedHandler, projectDeletedHandler, + projectUpdatedKafkaHandler } from './projects'; import { projectMemberAddedHandler, projectMemberRemovedHandler, projectMemberUpdatedHandler } from './projectMembers'; import { projectAttachmentAddedHandler, projectAttachmentRemovedHandler, @@ -12,7 +13,7 @@ import { phaseProductAddedHandler, phaseProductRemovedHandler, import { timelineAddedHandler, timelineUpdatedHandler, timelineRemovedHandler } from './timelines'; import { milestoneAddedHandler, milestoneUpdatedHandler, milestoneRemovedHandler } from './milestones'; -export default { +export const rabbitHandlers = { 'project.initial': projectCreatedHandler, [EVENT.ROUTING_KEY.PROJECT_DRAFT_CREATED]: projectCreatedHandler, [EVENT.ROUTING_KEY.PROJECT_UPDATED]: projectUpdatedHandler, @@ -42,3 +43,17 @@ export default { [EVENT.ROUTING_KEY.MILESTONE_REMOVED]: milestoneRemovedHandler, [EVENT.ROUTING_KEY.MILESTONE_UPDATED]: milestoneUpdatedHandler, }; + +export const kafkaHandlers = { + // Events defined by project-service + [BUS_API_EVENT.PROJECT_UPDATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.PROJECT_FILES_UPDATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.PROJECT_TEAM_UPDATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.PROJECT_PLAN_UPDATED]: projectUpdatedKafkaHandler, + + // Events from message-service + [BUS_API_EVENT.TOPIC_CREATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.TOPIC_UPDATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.POST_CREATED]: projectUpdatedKafkaHandler, + [BUS_API_EVENT.POST_UPDATED]: projectUpdatedKafkaHandler, +}; diff --git a/src/events/projects/index.js b/src/events/projects/index.js index 64a7690a..7b247a29 100644 --- a/src/events/projects/index.js +++ b/src/events/projects/index.js @@ -2,10 +2,13 @@ * Event handlers for project create, update and delete */ import _ from 'lodash'; +import Joi from 'joi'; import Promise from 'bluebird'; import config from 'config'; import util from '../../util'; +import models from '../../models'; import { createPhaseTopic } from '../projectPhases'; +import { REGEX } from '../../constants'; const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); @@ -128,8 +131,61 @@ const projectDeletedHandler = Promise.coroutine(function* (logger, msg, channel) } }); +/** + * Kafka event handlers + */ + +const payloadSchema = Joi.object().keys({ + projectId: Joi.number().integer().positive().required(), + projectName: Joi.string().required(), + projectUrl: Joi.string().regex(REGEX.URL).required(), + userId: Joi.number().integer().positive().required(), + initiatorUserId: Joi.number().integer().positive().required(), +}).required(); + +/** + * Updates project activity fields. throws exceptions in case of error + * @param {Object} app Application object used to interact with RMQ service + * @param {String} topic Kafka topic + * @param {Object} payload Message payload + * @return {Promise} Promise + */ +async function projectUpdatedKafkaHandler(app, topic, payload) { + // Validate payload + const result = Joi.validate(payload, payloadSchema); + if (result.error) { + throw new Error(result.error); + } + + // Find project by id and update activity. Single update is used as there is no need to wrap it into transaction + const projectId = payload.projectId; + const project = await models.Project.findById(projectId); + if (!project) { + throw new Error(`Project with id ${projectId} not found`); + } + const previousValue = project.get({ plain: true }); + project.lastActivityAt = new Date(); + project.lastActivityUserId = payload.initiatorUserId; + + await project.save(); + + // first get the existing document and than merge the updated changes and save the new document + const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); + const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle + // update the merged document + await eClient.update({ + index: ES_PROJECT_INDEX, + type: ES_PROJECT_TYPE, + id: previousValue.id, + body: { + doc: merged, + }, + }); +} + module.exports = { projectCreatedHandler, projectUpdatedHandler, projectDeletedHandler, + projectUpdatedKafkaHandler, }; diff --git a/src/services/kafka/updateProjectActivity.spec.js b/src/events/projects/index.spec.js similarity index 67% rename from src/services/kafka/updateProjectActivity.spec.js rename to src/events/projects/index.spec.js index b52cb6e2..ce5a8072 100644 --- a/src/services/kafka/updateProjectActivity.spec.js +++ b/src/events/projects/index.spec.js @@ -3,12 +3,12 @@ import _ from 'lodash'; import sinon from 'sinon'; import chai, { expect } from 'chai'; import models from '../../models'; -import updateProjectActivity from './updateProjectActivity'; +import { projectUpdatedKafkaHandler } from './index'; import testUtil from '../../tests/util'; chai.use(require('chai-as-promised')); -describe('updateProjectActivity', () => { +describe('projectUpdatedKafkaHandler', () => { // Any topic name is fine here as routing happens in kafkaConsumer const topic = 'topic'; @@ -29,60 +29,61 @@ describe('updateProjectActivity', () => { }; it('should throw exception when payload is not a valid json', async () => { - await expect(updateProjectActivity(mockedApp, topic, 'string')).to.be.rejectedWith(SyntaxError); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, 'string')).to.be.rejectedWith(SyntaxError); }); it('should throw validation exception when payload is empty', async () => { - await expect(updateProjectActivity(mockedApp, topic, '{}')).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, '{}')).to.be.rejectedWith(Error); }); it('should throw validation exception when projectId is not set', async () => { const payload = _.omit(validPayload, 'projectId'); - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when projectName is not set', async () => { const payload = _.omit(validPayload, 'projectName'); - await expect(updateProjectActivity(mockedApp, mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, mockedApp, topic, JSON.stringify(payload))) + .to.be.rejectedWith(Error); }); it('should throw validation exception when projectUrl is not set', async () => { const payload = _.omit(validPayload, 'projectUrl'); - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when userId is not set', async () => { const payload = _.omit(validPayload, 'userId'); - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when initiatorUserId is not set', async () => { const payload = _.omit(validPayload, 'initiatorUserId'); - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when projectId is not integer', async () => { const payload = _.clone(validPayload); payload.projectId = 'string'; - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when projectUrl is not a valid url', async () => { const payload = _.clone(validPayload); payload.projectUrl = 'string'; - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when userId is not integer', async () => { const payload = _.clone(validPayload); payload.userId = 'string'; - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); it('should throw validation exception when initiatorUserId is not integer', async () => { const payload = _.clone(validPayload); payload.initiatorUserId = 'string'; - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); }); describe('integration', () => { @@ -111,12 +112,12 @@ describe('updateProjectActivity', () => { it('should throw exception when project not found by id', async () => { const payload = _.clone(validPayload); payload.projectId = 2; - await expect(updateProjectActivity(mockedApp, topic, JSON.stringify(payload))).to.be + await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be .rejectedWith(Error, 'Project with id 2 not found'); }); it('should update lastActivityAt and lastActivityUserId columns in db', async () => { - await updateProjectActivity(mockedApp, topic, JSON.stringify(validPayload)); + await projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(validPayload)); const updatedProject = await models.Project.findById(project.id); expect(updatedProject.lastActivityUserId).to.be.eql(2); @@ -124,7 +125,7 @@ describe('updateProjectActivity', () => { }); it('should update ES index', async () => { - await updateProjectActivity(mockedApp, topic, JSON.stringify(validPayload)); + await projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(validPayload)); // Check that message has been sent to RabbitMQ as it updates ES expect(mockedApp.services.pubsub.publish).to.be.called; }); diff --git a/src/services/index.js b/src/services/index.js index a6bbd776..017a6ec2 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,8 +2,8 @@ import config from 'config'; import RabbitMQService from './rabbitmq'; -import startKafkaConsumer from './kafka/kafkaConsumer'; -import kafkaHandler from './kafka/kafkaHandlers'; +import startKafkaConsumer from './kafkaConsumer'; +import { kafkaHandlers } from '../events'; /** * Responsible for establishing connections to all external services @@ -33,7 +33,7 @@ module.exports = (fapp, logger) => { .then(() => { logger.info('RabbitMQ service initialized'); }) - .then(() => startKafkaConsumer(kafkaHandler, app, logger)) + .then(() => startKafkaConsumer(kafkaHandlers, app, logger)) .then(() => { logger.info('Kafka consumer service initialized'); }) diff --git a/src/services/kafka/kafkaHandlers.js b/src/services/kafka/kafkaHandlers.js deleted file mode 100644 index 728cbe8c..00000000 --- a/src/services/kafka/kafkaHandlers.js +++ /dev/null @@ -1,16 +0,0 @@ -import updateProjectActivity from './updateProjectActivity'; -import { BUS_API_EVENT } from '../../constants'; - -// Mapping of Kafka topic and handler -export default { - // Events defined by project-service - [BUS_API_EVENT.PROJECT_UPDATED]: updateProjectActivity, - [BUS_API_EVENT.PROJECT_FILES_UPDATED]: updateProjectActivity, - [BUS_API_EVENT.PROJECT_TEAM_UPDATED]: updateProjectActivity, - [BUS_API_EVENT.PROJECT_PLAN_UPDATED]: updateProjectActivity, - // Events from message-service - [BUS_API_EVENT.TOPIC_CREATED]: updateProjectActivity, - [BUS_API_EVENT.TOPIC_UPDATED]: updateProjectActivity, - [BUS_API_EVENT.POST_CREATED]: updateProjectActivity, - [BUS_API_EVENT.POST_UPDATED]: updateProjectActivity, -}; diff --git a/src/services/kafka/updateProjectActivity.js b/src/services/kafka/updateProjectActivity.js deleted file mode 100644 index 398433fd..00000000 --- a/src/services/kafka/updateProjectActivity.js +++ /dev/null @@ -1,58 +0,0 @@ -import _ from 'lodash'; -import Joi from 'joi'; -import config from 'config'; -import util from '../../util'; -import models from '../../models'; -import { REGEX } from '../../constants'; - -const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); -const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); -const eClient = util.getElasticSearchClient(); - -const payloadSchema = Joi.object().keys({ - projectId: Joi.number().integer().positive().required(), - projectName: Joi.string().required(), - projectUrl: Joi.string().regex(REGEX.URL).required(), - userId: Joi.number().integer().positive().required(), - initiatorUserId: Joi.number().integer().positive().required(), -}).required(); - -/** - * Updates project activity fields. throws exceptions in case of error - * @param {Object} app Application object used to interact with RMQ service - * @param {String} topic Kafka topic - * @param {Object} payload Message payload - * @return {Promise} Promise - */ -export default async function updateProjectActivity(app, topic, payload) { - // Validate payload - const result = Joi.validate(payload, payloadSchema); - if (result.error) { - throw new Error(result.error); - } - - // Find project by id and update activity. Single update is used as there is no need to wrap it into transaction - const projectId = payload.projectId; - const project = await models.Project.findById(projectId); - if (!project) { - throw new Error(`Project with id ${projectId} not found`); - } - const previousValue = project.get({ plain: true }); - project.lastActivityAt = new Date(); - project.lastActivityUserId = payload.initiatorUserId; - - await project.save(); - - // first get the existing document and than merge the updated changes and save the new document - const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); - const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle - // update the merged document - await eClient.update({ - index: ES_PROJECT_INDEX, - type: ES_PROJECT_TYPE, - id: previousValue.id, - body: { - doc: merged, - }, - }); -} diff --git a/src/services/kafka/kafkaConsumer.js b/src/services/kafkaConsumer.js similarity index 100% rename from src/services/kafka/kafkaConsumer.js rename to src/services/kafkaConsumer.js diff --git a/src/services/kafka/kafkaConsumer.spec.js b/src/services/kafkaConsumer.spec.js similarity index 100% rename from src/services/kafka/kafkaConsumer.spec.js rename to src/services/kafkaConsumer.spec.js diff --git a/src/services/rabbitmq.js b/src/services/rabbitmq.js index f2f9917f..bddcfab7 100644 --- a/src/services/rabbitmq.js +++ b/src/services/rabbitmq.js @@ -1,7 +1,7 @@ /* globals Promise */ import _ from 'lodash'; import amqplib from 'amqplib'; -import handlers from '../events'; +import { rabbitHandlers as handlers } from '../events'; module.exports = class RabbitMQService { From 53e66df16e27cb5c2091648d6013a1dce18a42e9 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 14:55:15 +0800 Subject: [PATCH 50/77] adjusted tests for projectUpdatedKafkaHandler --- src/events/projects/index.js | 29 +++++++++------- src/events/projects/index.spec.js | 55 +++++++++++++++++++------------ src/tests/util.js | 28 ++++++++++++++++ 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/src/events/projects/index.js b/src/events/projects/index.js index 7b247a29..505d7ddb 100644 --- a/src/events/projects/index.js +++ b/src/events/projects/index.js @@ -165,22 +165,27 @@ async function projectUpdatedKafkaHandler(app, topic, payload) { } const previousValue = project.get({ plain: true }); project.lastActivityAt = new Date(); - project.lastActivityUserId = payload.initiatorUserId; + project.lastActivityUserId = payload.initiatorUserId.toString(); await project.save(); // first get the existing document and than merge the updated changes and save the new document - const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); - const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle - // update the merged document - await eClient.update({ - index: ES_PROJECT_INDEX, - type: ES_PROJECT_TYPE, - id: previousValue.id, - body: { - doc: merged, - }, - }); + try { + const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: previousValue.id }); + const merged = _.merge(doc._source, project.get({ plain: true })); // eslint-disable-line no-underscore-dangle + // update the merged document + await eClient.update({ + index: ES_PROJECT_INDEX, + type: ES_PROJECT_TYPE, + id: previousValue.id, + body: { + doc: merged, + }, + }); + } catch (error) { + throw Error(`failed to updated project document in elasitcsearch index (projectId: ${previousValue.id})` + + `. Details: '${error}'.`); + } } module.exports = { diff --git a/src/events/projects/index.spec.js b/src/events/projects/index.spec.js index ce5a8072..3954c07c 100644 --- a/src/events/projects/index.spec.js +++ b/src/events/projects/index.spec.js @@ -2,10 +2,16 @@ import _ from 'lodash'; import sinon from 'sinon'; import chai, { expect } from 'chai'; +import config from 'config'; +import util from '../../util'; import models from '../../models'; import { projectUpdatedKafkaHandler } from './index'; import testUtil from '../../tests/util'; +const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); +const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); +const eClient = util.getElasticSearchClient(); + chai.use(require('chai-as-promised')); describe('projectUpdatedKafkaHandler', () => { @@ -28,62 +34,58 @@ describe('projectUpdatedKafkaHandler', () => { }, }; - it('should throw exception when payload is not a valid json', async () => { - await expect(projectUpdatedKafkaHandler(mockedApp, topic, 'string')).to.be.rejectedWith(SyntaxError); - }); - it('should throw validation exception when payload is empty', async () => { - await expect(projectUpdatedKafkaHandler(mockedApp, topic, '{}')).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, {})).to.be.rejectedWith(Error); }); it('should throw validation exception when projectId is not set', async () => { const payload = _.omit(validPayload, 'projectId'); - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when projectName is not set', async () => { const payload = _.omit(validPayload, 'projectName'); - await expect(projectUpdatedKafkaHandler(mockedApp, mockedApp, topic, JSON.stringify(payload))) + await expect(projectUpdatedKafkaHandler(mockedApp, mockedApp, topic, payload)) .to.be.rejectedWith(Error); }); it('should throw validation exception when projectUrl is not set', async () => { const payload = _.omit(validPayload, 'projectUrl'); - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when userId is not set', async () => { const payload = _.omit(validPayload, 'userId'); - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when initiatorUserId is not set', async () => { const payload = _.omit(validPayload, 'initiatorUserId'); - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when projectId is not integer', async () => { const payload = _.clone(validPayload); payload.projectId = 'string'; - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when projectUrl is not a valid url', async () => { const payload = _.clone(validPayload); payload.projectUrl = 'string'; - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when userId is not integer', async () => { const payload = _.clone(validPayload); payload.userId = 'string'; - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); it('should throw validation exception when initiatorUserId is not integer', async () => { const payload = _.clone(validPayload); payload.initiatorUserId = 'string'; - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be.rejectedWith(Error); + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be.rejectedWith(Error); }); describe('integration', () => { @@ -91,6 +93,7 @@ describe('projectUpdatedKafkaHandler', () => { beforeEach(async () => { await testUtil.clearDb(); + await testUtil.clearEs(); project = await models.Project.create({ type: 'generic', billingAccountId: 1, @@ -101,7 +104,15 @@ describe('projectUpdatedKafkaHandler', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', + }); + await eClient.index({ + index: ES_PROJECT_INDEX, + type: ES_PROJECT_TYPE, + id: project.id, + body: { + doc: project.get({ plain: true }), + }, }); }); @@ -112,22 +123,24 @@ describe('projectUpdatedKafkaHandler', () => { it('should throw exception when project not found by id', async () => { const payload = _.clone(validPayload); payload.projectId = 2; - await expect(projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(payload))).to.be + await expect(projectUpdatedKafkaHandler(mockedApp, topic, payload)).to.be .rejectedWith(Error, 'Project with id 2 not found'); }); it('should update lastActivityAt and lastActivityUserId columns in db', async () => { - await projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(validPayload)); + await projectUpdatedKafkaHandler(mockedApp, topic, validPayload); const updatedProject = await models.Project.findById(project.id); - expect(updatedProject.lastActivityUserId).to.be.eql(2); + expect(updatedProject.lastActivityUserId).to.be.eql('2'); expect(updatedProject.lastActivityAt).to.be.greaterThan(project.lastActivityAt); }); it('should update ES index', async () => { - await projectUpdatedKafkaHandler(mockedApp, topic, JSON.stringify(validPayload)); - // Check that message has been sent to RabbitMQ as it updates ES - expect(mockedApp.services.pubsub.publish).to.be.called; + await projectUpdatedKafkaHandler(mockedApp, topic, validPayload); + + const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: validPayload.projectId }); + expect(doc._source.lastActivityUserId).to.be.eql('2'); + expect(new Date(doc._source.lastActivityAt)).to.be.greaterThan(project.lastActivityAt); }); }); }); diff --git a/src/tests/util.js b/src/tests/util.js index 232c553d..81d0b842 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -1,9 +1,15 @@ /* eslint-disable max-len */ +import config from 'config'; +import util from '../util'; import models from '../models'; const jwt = require('jsonwebtoken'); +const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); +const ES_TIMELINE_INDEX = config.get('elasticsearchConfig.timelineIndexName'); +const esClient = util.getElasticSearchClient(); + export default { clearDb: done => models.sequelize.sync({ force: true }) .then(() => { @@ -31,4 +37,26 @@ export default { // Waits for 500ms and executes cb function wait: cb => setTimeout(cb, 500), + + // clears elastic search indexes + clearEs: () => { + esClient.indices.delete({ + index: ES_PROJECT_INDEX, + // we would want to ignore no such index error + ignore: [404], + }) + .then(() => esClient.indices.create({ + index: ES_PROJECT_INDEX, + updateAllTypes: true, + body: { + // WARNING unlike all other places for tests we don't use mapping to keep it easier, + // I think it's ok, but if there are any issues regarding it, a proper configuration for ES_PROJECT_INDEX + // has to be used, same like in `migrations/elasticsearch_sync.js` or `routes/admin/project-create-index.js` + mappings: { }, + }, + })) + // Re-create timeline index + .then(() => esClient.indices.delete({ index: ES_TIMELINE_INDEX, ignore: [404] })) + .then(() => esClient.indices.create({ index: ES_TIMELINE_INDEX })); + }, }; From 3a386fc306499c759d0bdc7f98644066ecc49f76 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 15:06:37 +0800 Subject: [PATCH 51/77] fix lint error --- src/events/projects/index.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/events/projects/index.spec.js b/src/events/projects/index.spec.js index 3954c07c..60b9462d 100644 --- a/src/events/projects/index.spec.js +++ b/src/events/projects/index.spec.js @@ -139,8 +139,9 @@ describe('projectUpdatedKafkaHandler', () => { await projectUpdatedKafkaHandler(mockedApp, topic, validPayload); const doc = await eClient.get({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: validPayload.projectId }); - expect(doc._source.lastActivityUserId).to.be.eql('2'); - expect(new Date(doc._source.lastActivityAt)).to.be.greaterThan(project.lastActivityAt); + const esProject = doc._source; // eslint-disable-line no-underscore-dangle + expect(esProject.lastActivityUserId).to.be.eql('2'); + expect(new Date(esProject.lastActivityAt)).to.be.greaterThan(project.lastActivityAt); }); }); }); From 638dab364aa675c53b4a942453a414ce6e813f5e Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 16:04:04 +0800 Subject: [PATCH 52/77] fix tests for kafkaConsumer --- src/services/kafkaConsumer.spec.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/services/kafkaConsumer.spec.js b/src/services/kafkaConsumer.spec.js index 0be86d34..58090a64 100644 --- a/src/services/kafkaConsumer.spec.js +++ b/src/services/kafkaConsumer.spec.js @@ -1,8 +1,10 @@ /* eslint-disable no-unused-expressions */ import sinon from 'sinon'; import * as Kafka from 'no-kafka'; +import chai from 'chai'; import startKafkaConsumer from './kafkaConsumer'; +const should = chai.should(); describe('Kafka service', () => { const sandbox = sinon.sandbox.create(); @@ -57,6 +59,7 @@ describe('Kafka service', () => { beforeEach(async () => { mockedLogger.error.reset(); + mockedLogger.info.reset(); handlers = { topic1: sinon.stub(), @@ -70,7 +73,11 @@ describe('Kafka service', () => { it('calls handler for specific topic only', async () => { await consumerFunction([{ message: { - value: 'message', + value: `{ + "payload": { + "prop": "message" + } + }`, }, }], 'topic1', {}); @@ -83,7 +90,11 @@ describe('Kafka service', () => { await consumerFunction([{ message: { - value: 'message', + value: `{ + "payload": { + "prop": "message" + } + }`, }, }], 'topic2', {}); @@ -95,7 +106,11 @@ describe('Kafka service', () => { it('drops message when handler not found', async () => { await consumerFunction([{ message: { - value: 'message', + value: `{ + "payload": { + "prop": "message" + } + }`, }, }], 'unknown-topic', {}); From 5fbcb916a8f28119af12bfa2b42359133e667cbd Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 16:09:28 +0800 Subject: [PATCH 53/77] fix lint error --- src/services/kafkaConsumer.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/kafkaConsumer.spec.js b/src/services/kafkaConsumer.spec.js index 58090a64..887a6b16 100644 --- a/src/services/kafkaConsumer.spec.js +++ b/src/services/kafkaConsumer.spec.js @@ -4,7 +4,8 @@ import * as Kafka from 'no-kafka'; import chai from 'chai'; import startKafkaConsumer from './kafkaConsumer'; -const should = chai.should(); + +chai.should(); describe('Kafka service', () => { const sandbox = sinon.sandbox.create(); From b7cd0c240a0694b207bf199fab91d6259c83a91b Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 17:50:01 +0800 Subject: [PATCH 54/77] fix projectUpdatedKafkaHandler to use test ES properly --- src/events/projects/index.spec.js | 7 ++++--- src/tests/util.js | 28 ---------------------------- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/src/events/projects/index.spec.js b/src/events/projects/index.spec.js index 60b9462d..9067166e 100644 --- a/src/events/projects/index.spec.js +++ b/src/events/projects/index.spec.js @@ -7,6 +7,7 @@ import util from '../../util'; import models from '../../models'; import { projectUpdatedKafkaHandler } from './index'; import testUtil from '../../tests/util'; +import server from '../../app'; const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType'); @@ -93,7 +94,6 @@ describe('projectUpdatedKafkaHandler', () => { beforeEach(async () => { await testUtil.clearDb(); - await testUtil.clearEs(); project = await models.Project.create({ type: 'generic', billingAccountId: 1, @@ -106,7 +106,8 @@ describe('projectUpdatedKafkaHandler', () => { lastActivityAt: 1, lastActivityUserId: '1', }); - await eClient.index({ + // add project to ES index + await server.services.es.index({ index: ES_PROJECT_INDEX, type: ES_PROJECT_TYPE, id: project.id, @@ -116,7 +117,7 @@ describe('projectUpdatedKafkaHandler', () => { }); }); - afterEach(async () => { + after(async () => { await testUtil.clearDb(); }); diff --git a/src/tests/util.js b/src/tests/util.js index 81d0b842..232c553d 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -1,15 +1,9 @@ /* eslint-disable max-len */ -import config from 'config'; -import util from '../util'; import models from '../models'; const jwt = require('jsonwebtoken'); -const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName'); -const ES_TIMELINE_INDEX = config.get('elasticsearchConfig.timelineIndexName'); -const esClient = util.getElasticSearchClient(); - export default { clearDb: done => models.sequelize.sync({ force: true }) .then(() => { @@ -37,26 +31,4 @@ export default { // Waits for 500ms and executes cb function wait: cb => setTimeout(cb, 500), - - // clears elastic search indexes - clearEs: () => { - esClient.indices.delete({ - index: ES_PROJECT_INDEX, - // we would want to ignore no such index error - ignore: [404], - }) - .then(() => esClient.indices.create({ - index: ES_PROJECT_INDEX, - updateAllTypes: true, - body: { - // WARNING unlike all other places for tests we don't use mapping to keep it easier, - // I think it's ok, but if there are any issues regarding it, a proper configuration for ES_PROJECT_INDEX - // has to be used, same like in `migrations/elasticsearch_sync.js` or `routes/admin/project-create-index.js` - mappings: { }, - }, - })) - // Re-create timeline index - .then(() => esClient.indices.delete({ index: ES_TIMELINE_INDEX, ignore: [404] })) - .then(() => esClient.indices.create({ index: ES_TIMELINE_INDEX })); - }, }; From 652e0dc50f731d2ff1dffb733ef66295e28a95c4 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 17:51:23 +0800 Subject: [PATCH 55/77] fix test broken after lastActivityUserId altered to be a string instead of integer --- src/routes/projects/create.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/projects/create.spec.js b/src/routes/projects/create.spec.js index af442d8e..5810401e 100644 --- a/src/routes/projects/create.spec.js +++ b/src/routes/projects/create.spec.js @@ -336,7 +336,7 @@ describe('Project create', () => { resJson.bookmarks[0].title.should.be.eql('title1'); resJson.bookmarks[0].address.should.be.eql('http://www.address.com'); // Check that activity fields are set - resJson.lastActivityUserId.should.be.eql(40051331); + resJson.lastActivityUserId.should.be.eql('40051331'); resJson.lastActivityAt.should.be.not.null; server.services.pubsub.publish.calledWith('project.draft-created').should.be.true; done(); From cc7bd42536dd2614b3288f0c384994d6ce6c6e7f Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 18:00:00 +0800 Subject: [PATCH 56/77] make sure that lastActivityUserId is set as a string on project create --- src/routes/projects/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/projects/create.js b/src/routes/projects/create.js index d84d2ec0..2600203b 100644 --- a/src/routes/projects/create.js +++ b/src/routes/projects/create.js @@ -219,7 +219,7 @@ module.exports = [ createdBy: req.authUser.userId, updatedBy: req.authUser.userId, lastActivityAt: new Date(), - lastActivityUserId: req.authUser.userId, + lastActivityUserId: req.authUser.userId.toString(10), members: [{ isPrimary: true, role: userRole, From 09e3f4d88f3eece71f6db223128888287e86c100 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 19 Sep 2018 18:11:35 +0800 Subject: [PATCH 57/77] adjust test to use set lastActivityUserId as a sting even though these places doesn't break any tests --- src/routes/attachments/create.spec.js | 2 +- src/routes/attachments/delete.spec.js | 2 +- src/routes/attachments/update.spec.js | 2 +- src/routes/milestones/create.spec.js | 4 ++-- src/routes/milestones/delete.spec.js | 4 ++-- src/routes/milestones/get.spec.js | 4 ++-- src/routes/milestones/list.spec.js | 4 ++-- src/routes/milestones/update.spec.js | 4 ++-- src/routes/phaseProducts/create.spec.js | 2 +- src/routes/phaseProducts/delete.spec.js | 2 +- src/routes/phaseProducts/get.spec.js | 2 +- src/routes/phaseProducts/list.spec.js | 2 +- src/routes/phaseProducts/update.spec.js | 2 +- src/routes/phases/create.spec.js | 2 +- src/routes/phases/delete.spec.js | 2 +- src/routes/phases/get.spec.js | 2 +- src/routes/phases/list.spec.js | 2 +- src/routes/phases/update.spec.js | 2 +- src/routes/projectMembers/create.spec.js | 4 ++-- src/routes/projectMembers/delete.spec.js | 2 +- src/routes/projectMembers/update.spec.js | 2 +- src/routes/projectUpgrade/create.spec.js | 2 +- src/routes/projects/delete.spec.js | 2 +- src/routes/projects/get.spec.js | 4 ++-- src/routes/projects/list-db.spec.js | 6 +++--- src/routes/projects/list.spec.js | 12 ++++++------ src/routes/projects/update.spec.js | 6 +++--- src/routes/timelines/create.spec.js | 4 ++-- src/routes/timelines/delete.spec.js | 4 ++-- src/routes/timelines/get.spec.js | 4 ++-- src/routes/timelines/list.spec.js | 4 ++-- src/routes/timelines/update.spec.js | 4 ++-- 32 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/routes/attachments/create.spec.js b/src/routes/attachments/create.spec.js index 96617792..5cbae3fb 100644 --- a/src/routes/attachments/create.spec.js +++ b/src/routes/attachments/create.spec.js @@ -72,7 +72,7 @@ describe('Project Attachments', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/attachments/delete.spec.js b/src/routes/attachments/delete.spec.js index 7b26193b..b9366d2d 100644 --- a/src/routes/attachments/delete.spec.js +++ b/src/routes/attachments/delete.spec.js @@ -29,7 +29,7 @@ describe('Project Attachments delete', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/attachments/update.spec.js b/src/routes/attachments/update.spec.js index c181392f..99dd0b15 100644 --- a/src/routes/attachments/update.spec.js +++ b/src/routes/attachments/update.spec.js @@ -28,7 +28,7 @@ describe('Project Attachments update', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/milestones/create.spec.js b/src/routes/milestones/create.spec.js index dba44d71..bf652b28 100644 --- a/src/routes/milestones/create.spec.js +++ b/src/routes/milestones/create.spec.js @@ -32,7 +32,7 @@ describe('CREATE milestone', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -44,7 +44,7 @@ describe('CREATE milestone', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ], { returning: true }) diff --git a/src/routes/milestones/delete.spec.js b/src/routes/milestones/delete.spec.js index dbde7dab..ee9c41e3 100644 --- a/src/routes/milestones/delete.spec.js +++ b/src/routes/milestones/delete.spec.js @@ -53,7 +53,7 @@ describe('DELETE milestone', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -65,7 +65,7 @@ describe('DELETE milestone', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/milestones/get.spec.js b/src/routes/milestones/get.spec.js index 39ede27d..fb0451d1 100644 --- a/src/routes/milestones/get.spec.js +++ b/src/routes/milestones/get.spec.js @@ -25,7 +25,7 @@ describe('GET milestone', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -37,7 +37,7 @@ describe('GET milestone', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/milestones/list.spec.js b/src/routes/milestones/list.spec.js index 56b48d0c..21c07336 100644 --- a/src/routes/milestones/list.spec.js +++ b/src/routes/milestones/list.spec.js @@ -95,7 +95,7 @@ describe('LIST timelines', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -107,7 +107,7 @@ describe('LIST timelines', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/milestones/update.spec.js b/src/routes/milestones/update.spec.js index 15ac4eb0..6286c5d4 100644 --- a/src/routes/milestones/update.spec.js +++ b/src/routes/milestones/update.spec.js @@ -30,7 +30,7 @@ describe('UPDATE Milestone', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -42,7 +42,7 @@ describe('UPDATE Milestone', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/phaseProducts/create.spec.js b/src/routes/phaseProducts/create.spec.js index e3e23f47..8f81a28b 100644 --- a/src/routes/phaseProducts/create.spec.js +++ b/src/routes/phaseProducts/create.spec.js @@ -51,7 +51,7 @@ describe('Phase Products', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phaseProducts/delete.spec.js b/src/routes/phaseProducts/delete.spec.js index 99c62051..69942fa6 100644 --- a/src/routes/phaseProducts/delete.spec.js +++ b/src/routes/phaseProducts/delete.spec.js @@ -79,7 +79,7 @@ describe('Phase Products', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phaseProducts/get.spec.js b/src/routes/phaseProducts/get.spec.js index b0fd774e..a71d8a7f 100644 --- a/src/routes/phaseProducts/get.spec.js +++ b/src/routes/phaseProducts/get.spec.js @@ -52,7 +52,7 @@ describe('Phase Products', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phaseProducts/list.spec.js b/src/routes/phaseProducts/list.spec.js index 566c63a7..8dcd08d2 100644 --- a/src/routes/phaseProducts/list.spec.js +++ b/src/routes/phaseProducts/list.spec.js @@ -58,7 +58,7 @@ describe('Phase Products', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; project = p.toJSON(); diff --git a/src/routes/phaseProducts/update.spec.js b/src/routes/phaseProducts/update.spec.js index 37fbe09d..3c35871b 100644 --- a/src/routes/phaseProducts/update.spec.js +++ b/src/routes/phaseProducts/update.spec.js @@ -65,7 +65,7 @@ describe('Phase Products', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phases/create.spec.js b/src/routes/phases/create.spec.js index 49c61924..8bdcb5d8 100644 --- a/src/routes/phases/create.spec.js +++ b/src/routes/phases/create.spec.js @@ -68,7 +68,7 @@ describe('Project Phases', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; projectName = p.name; diff --git a/src/routes/phases/delete.spec.js b/src/routes/phases/delete.spec.js index b180d6b9..78453f39 100644 --- a/src/routes/phases/delete.spec.js +++ b/src/routes/phases/delete.spec.js @@ -84,7 +84,7 @@ describe('Project Phases', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; projectName = p.name; diff --git a/src/routes/phases/get.spec.js b/src/routes/phases/get.spec.js index 8a82601d..f6cde663 100644 --- a/src/routes/phases/get.spec.js +++ b/src/routes/phases/get.spec.js @@ -53,7 +53,7 @@ describe('Project Phases', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; // create members diff --git a/src/routes/phases/list.spec.js b/src/routes/phases/list.spec.js index ce94ed25..43a1d14f 100644 --- a/src/routes/phases/list.spec.js +++ b/src/routes/phases/list.spec.js @@ -59,7 +59,7 @@ describe('Project Phases', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; project = p.toJSON(); diff --git a/src/routes/phases/update.spec.js b/src/routes/phases/update.spec.js index 5a3f2e86..ae9bd30c 100644 --- a/src/routes/phases/update.spec.js +++ b/src/routes/phases/update.spec.js @@ -82,7 +82,7 @@ describe('Project Phases', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { projectId = p.id; projectName = p.name; diff --git a/src/routes/projectMembers/create.spec.js b/src/routes/projectMembers/create.spec.js index 2c3fd5ea..429b50de 100644 --- a/src/routes/projectMembers/create.spec.js +++ b/src/routes/projectMembers/create.spec.js @@ -30,7 +30,7 @@ describe('Project Members create', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members @@ -53,7 +53,7 @@ describe('Project Members create', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p2) => { project2 = p2; done(); diff --git a/src/routes/projectMembers/delete.spec.js b/src/routes/projectMembers/delete.spec.js index 7e7d57c7..9f539ef2 100644 --- a/src/routes/projectMembers/delete.spec.js +++ b/src/routes/projectMembers/delete.spec.js @@ -49,7 +49,7 @@ describe('Project members delete', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/projectMembers/update.spec.js b/src/routes/projectMembers/update.spec.js index a92144d7..4db550cd 100644 --- a/src/routes/projectMembers/update.spec.js +++ b/src/routes/projectMembers/update.spec.js @@ -31,7 +31,7 @@ describe('Project members update', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/projectUpgrade/create.spec.js b/src/routes/projectUpgrade/create.spec.js index 1ec7859b..41ee7e0b 100644 --- a/src/routes/projectUpgrade/create.spec.js +++ b/src/routes/projectUpgrade/create.spec.js @@ -41,7 +41,7 @@ describe('Project upgrade', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', version: 'v2', directProjectId: 123, estimatedPrice: 15000, diff --git a/src/routes/projects/delete.spec.js b/src/routes/projects/delete.spec.js index 8649edc7..0ade73b7 100644 --- a/src/routes/projects/delete.spec.js +++ b/src/routes/projects/delete.spec.js @@ -49,7 +49,7 @@ describe('Project delete test', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members diff --git a/src/routes/projects/get.spec.js b/src/routes/projects/get.spec.js index 027c8851..08fd3bbf 100644 --- a/src/routes/projects/get.spec.js +++ b/src/routes/projects/get.spec.js @@ -26,7 +26,7 @@ describe('GET Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members @@ -59,7 +59,7 @@ describe('GET Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project2 = p; }); diff --git a/src/routes/projects/list-db.spec.js b/src/routes/projects/list-db.spec.js index d47a4b47..08f4d14c 100644 --- a/src/routes/projects/list-db.spec.js +++ b/src/routes/projects/list-db.spec.js @@ -63,7 +63,7 @@ describe('LIST Project db', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members @@ -105,7 +105,7 @@ describe('LIST Project db', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 2, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project2 = p; return models.ProjectMember.create({ @@ -127,7 +127,7 @@ describe('LIST Project db', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 3, - lastActivityUserId: 1, + lastActivityUserId: '1', }); return Promise.all([p1, p2, p3]) .then(() => done()); diff --git a/src/routes/projects/list.spec.js b/src/routes/projects/list.spec.js index 02ca46f9..ef1bb7c6 100644 --- a/src/routes/projects/list.spec.js +++ b/src/routes/projects/list.spec.js @@ -30,7 +30,7 @@ const data = [ createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', members: [ { id: 1, @@ -76,7 +76,7 @@ const data = [ createdBy: 1, updatedBy: 1, lastActivityAt: 2, - lastActivityUserId: 1, + lastActivityUserId: '1', members: [ { id: 1, @@ -100,7 +100,7 @@ const data = [ createdBy: 1, updatedBy: 1, lastActivityAt: 3, - lastActivityUserId: 1, + lastActivityUserId: '1', }, ]; @@ -126,7 +126,7 @@ describe('LIST Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project1 = p; // create members @@ -168,7 +168,7 @@ describe('LIST Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 2, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project2 = p; return models.ProjectMember.create({ @@ -190,7 +190,7 @@ describe('LIST Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 3, - lastActivityUserId: 1, + lastActivityUserId: '1', }).then((p) => { project3 = p; return Promise.resolve(); diff --git a/src/routes/projects/update.spec.js b/src/routes/projects/update.spec.js index e76cb0af..f1cb51ef 100644 --- a/src/routes/projects/update.spec.js +++ b/src/routes/projects/update.spec.js @@ -67,7 +67,7 @@ describe('Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }, { @@ -80,7 +80,7 @@ describe('Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }, { @@ -92,7 +92,7 @@ describe('Project', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', createdAt: '2016-06-30 00:33:07+00', updatedAt: '2016-06-30 00:33:07+00', }]) diff --git a/src/routes/timelines/create.spec.js b/src/routes/timelines/create.spec.js index 2c8bfcde..41590c37 100644 --- a/src/routes/timelines/create.spec.js +++ b/src/routes/timelines/create.spec.js @@ -23,7 +23,7 @@ const testProjects = [ createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -36,7 +36,7 @@ const testProjects = [ updatedBy: 2, deletedAt: '2018-05-15T00:00:00Z', lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, ]; diff --git a/src/routes/timelines/delete.spec.js b/src/routes/timelines/delete.spec.js index 4b0b76f3..68c505b2 100644 --- a/src/routes/timelines/delete.spec.js +++ b/src/routes/timelines/delete.spec.js @@ -52,7 +52,7 @@ describe('DELETE timeline', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -64,7 +64,7 @@ describe('DELETE timeline', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/get.spec.js b/src/routes/timelines/get.spec.js index 39eb4bc6..2331295c 100644 --- a/src/routes/timelines/get.spec.js +++ b/src/routes/timelines/get.spec.js @@ -73,7 +73,7 @@ describe('GET timeline', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -85,7 +85,7 @@ describe('GET timeline', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/list.spec.js b/src/routes/timelines/list.spec.js index d8cda262..2c1395c8 100644 --- a/src/routes/timelines/list.spec.js +++ b/src/routes/timelines/list.spec.js @@ -113,7 +113,7 @@ describe('LIST timelines', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -125,7 +125,7 @@ describe('LIST timelines', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ]) diff --git a/src/routes/timelines/update.spec.js b/src/routes/timelines/update.spec.js index 19eecd6f..4cb811a9 100644 --- a/src/routes/timelines/update.spec.js +++ b/src/routes/timelines/update.spec.js @@ -77,7 +77,7 @@ describe('UPDATE timeline', () => { createdBy: 1, updatedBy: 1, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', }, { type: 'generic', @@ -89,7 +89,7 @@ describe('UPDATE timeline', () => { createdBy: 2, updatedBy: 2, lastActivityAt: 1, - lastActivityUserId: 1, + lastActivityUserId: '1', deletedAt: '2018-05-15T00:00:00Z', }, ], { returning: true }) From 3e380f3af0de4bdb2cc1cfa90e7ce59425e99708 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 19 Sep 2018 16:09:48 +0530 Subject: [PATCH 58/77] Increasing the timeout --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5257963c..b0a7fe22 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "prestart": "npm run -s build", "start": "node dist", "start:dev": "NODE_ENV=development PORT=8001 nodemon -w src --exec \"babel-node src --presets es2015\" | ./node_modules/.bin/bunyan", - "test": "NODE_ENV=test npm run lint && NODE_ENV=test npm run sync:es && NODE_ENV=test npm run sync:db && NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 5000 --compilers js:babel-core/register $(find src -path '*spec.js*')", + "test": "NODE_ENV=test npm run lint && NODE_ENV=test npm run sync:es && NODE_ENV=test npm run sync:db && NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 7000 --compilers js:babel-core/register $(find src -path '*spec.js*')", "test:watch": "NODE_ENV=test ./node_modules/.bin/mocha -w --compilers js:babel-core/register $(find src -path '*spec.js*')", "seed": "babel-node src/tests/seed.js --presets es2015" }, From 50dcf679af8105969c83d70b9f60f2ad4b5e72d0 Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Wed, 19 Sep 2018 16:15:41 +0530 Subject: [PATCH 59/77] Updated readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12b2310f..60abab9e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Microservice to manage CRUD operations for all things Projects. Copy config/sample.local.js as config/local.js, update the properties and according to your env setup #### Database -Once you start your PostgreSQL database through docker, it will create a projectsDB. +Once you start your PostgreSQL database through docker, it will create a projectsdb. *To create tables - note this will drop tables if they already exist* ``` NODE_ENV=development npm run sync:db From 8b2f292fa30c9efb356b4617e16f11a5b3897f49 Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Wed, 19 Sep 2018 16:22:19 +0530 Subject: [PATCH 60/77] Increased timeout to 10000ms --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b0a7fe22..008d7ad1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "prestart": "npm run -s build", "start": "node dist", "start:dev": "NODE_ENV=development PORT=8001 nodemon -w src --exec \"babel-node src --presets es2015\" | ./node_modules/.bin/bunyan", - "test": "NODE_ENV=test npm run lint && NODE_ENV=test npm run sync:es && NODE_ENV=test npm run sync:db && NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 7000 --compilers js:babel-core/register $(find src -path '*spec.js*')", + "test": "NODE_ENV=test npm run lint && NODE_ENV=test npm run sync:es && NODE_ENV=test npm run sync:db && NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 10000 --compilers js:babel-core/register $(find src -path '*spec.js*')", "test:watch": "NODE_ENV=test ./node_modules/.bin/mocha -w --compilers js:babel-core/register $(find src -path '*spec.js*')", "seed": "babel-node src/tests/seed.js --presets es2015" }, From 7366e207f495f2edc7c4b9da299b2091e7bf080e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 19 Sep 2018 17:31:16 +0530 Subject: [PATCH 61/77] Refactored logic for throwing plan update event only for duration, startDate and specific status changes --- src/events/busApi.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/events/busApi.js b/src/events/busApi.js index ac471a0e..f30cbb5b 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -386,32 +386,31 @@ module.exports = (app, logger) => { where: { id: projectId }, }) .then((project) => { - // send PROJECT_PLAN_UPDATED Kafka message when one of the specified below properties changed - const watchProperties = ['spentBudget', 'progress', 'details', 'status', 'budget', 'startDate', 'duration']; - if (!_.isEqual(_.pick(original, watchProperties), - _.pick(updated, watchProperties))) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { - projectId, - projectName: project.name, - projectUrl: connectProjectUrl(projectId), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - } - + logger.debug(`Fetched project ${projectId} for the phase ${phaseId}`); + const eventsMap = {}; [ + ['duration', BUS_API_EVENT.PROJECT_PLAN_UPDATED], + ['startDate', BUS_API_EVENT.PROJECT_PLAN_UPDATED], ['spentBudget', BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT], ['progress', [BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS, BUS_API_EVENT.PROJECT_PROGRESS_MODIFIED]], ['details', BUS_API_EVENT.PROJECT_PHASE_UPDATE_SCOPE], ['status', BUS_API_EVENT.PROJECT_PHASE_TRANSITION_ACTIVE, PROJECT_PHASE_STATUS.ACTIVE], ['status', BUS_API_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED, PROJECT_PHASE_STATUS.COMPLETED], - ].forEach(([key, events, sendIfEqual]) => { + // ideally we should validate the old value being 'DRAFT' but there is no other status from which + // we can move phase to REVIEWED status + ['status', BUS_API_EVENT.PROJECT_PLAN_UPDATED, PROJECT_PHASE_STATUS.REVIEWED], + // ideally we should validate the old value being 'REVIEWED' but there is no other status from which + // we can move phase to DRAFT status + ['status', BUS_API_EVENT.PROJECT_PLAN_UPDATED, PROJECT_PHASE_STATUS.DRAFT], + ].forEach(([key, events, sendIfNewEqual]) => { // eslint-disable-next-line no-param-reassign events = Array.isArray(events) ? events : [events]; + // eslint-disable-next-line no-param-reassign + events = _.filter(events, e => !eventsMap[e]); - // send event(s) only if the target field's value was updated, or when an update matches a "sendIfEqual" value - if ((!sendIfEqual && !_.isEqual(original[key], updated[key])) || - (original[key] !== sendIfEqual && updated[key] === sendIfEqual)) { + // send event(s) only if the target field's value was updated, or when an update matches a "sendIfNewEqual" value + if ((!sendIfNewEqual && !_.isEqual(original[key], updated[key])) || + (original[key] !== sendIfNewEqual && updated[key] === sendIfNewEqual)) { events.forEach(event => createEvent(event, { projectId, phaseId, @@ -421,6 +420,7 @@ module.exports = (app, logger) => { userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger)); + events.forEach((event) => { eventsMap[event] = true; }); } }); From 1188ba2cd1e9d14eb73932c34525e5e4c0b9f627 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 19 Sep 2018 17:46:44 +0530 Subject: [PATCH 62/77] Picking Kafka config from circle CI env variables --- config/custom-environment-variables.json | 3 ++- deploy.sh | 22 +++++++++++++++++++++- src/services/kafkaConsumer.js | 6 +++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 41fc3eb3..17ee7bf4 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -30,7 +30,8 @@ "minPoolSize": "DB_MIN_POOL_SIZE" }, "kafkaConfig": { - "hosts": "KAFKA_HOSTS", + "groupdId": "KAFKA_GROUP_ID", + "url": "KAFKA_URL", "clientCert": "KAFKA_CLIENT_CERT", "clientCertKey": "KAFKA_CLIENT_CERT_KEY" }, diff --git a/deploy.sh b/deploy.sh index 7390c60b..80bc15c0 100755 --- a/deploy.sh +++ b/deploy.sh @@ -176,6 +176,22 @@ make_task_def(){ { "name": "TOKEN_CACHE_TIME", "value": "%s" + }, + { + "name": "KAFKA_CLIENT_CERT", + "value": "%s" + }, + { + "name": "KAFKA_CLIENT_CERT_KEY", + "value": "%s" + }, + { + "name": "KAFKA_GROUP_ID", + "value": "%s" + }, + { + "name": "KAFKA_URL", + "value": "%s" } ], "portMappings": [ @@ -226,9 +242,13 @@ make_task_def(){ AUTH0_CLIENT_ID=$(eval "echo \$${ENV}_AUTH0_CLIENT_ID") AUTH0_CLIENT_SECRET=$(eval "echo \$${ENV}_AUTH0_CLIENT_SECRET") TOKEN_CACHE_TIME=$(eval "echo \$${ENV}_TOKEN_CACHE_TIME") + KAFKA_CLIENT_CERT=$(eval "echo \$${ENV}_KAFKA_CLIENT_CERT") + KAFKA_CLIENT_CERT_KEY=$(eval "echo \$${ENV}_KAFKA_CLIENT_CERT_KEY") + KAFKA_GROUP_ID=$(eval "echo \$${ENV}_KAFKA_GROUP_ID") + KAFKA_URL=$(eval "echo \$${ENV}_KAFKA_URL") - task_def=$(printf "$task_template" $family $ACCOUNT_ID $AWS_ECS_CONTAINER_NAME $ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $CIRCLE_SHA1 $NODE_ENV $LOG_LEVEL $CAPTURE_LOGS $LOGENTRIES_TOKEN $API_VERSION $AWS_REGION $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY $AUTH_DOMAIN $AUTH_SECRET $VALID_ISSUERS $DB_MASTER_URL $MEMBER_SERVICE_ENDPOINT $IDENTITY_SERVICE_ENDPOINT $BUS_API_URL $MESSAGE_SERVICE_URL $SYSTEM_USER_CLIENT_ID $SYSTEM_USER_CLIENT_SECRET $PROJECTS_ES_URL $PROJECTS_ES_INDEX_NAME $RABBITMQ_URL $DIRECT_PROJECT_SERVICE_ENDPOINT $FILE_SERVICE_ENDPOINT $CONNECT_PROJECTS_URL $SEGMENT_ANALYTICS_KEY "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME $PORT $PORT $AWS_ECS_CLUSTER $AWS_REGION $NODE_ENV) + task_def=$(printf "$task_template" $family $ACCOUNT_ID $AWS_ECS_CONTAINER_NAME $ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $CIRCLE_SHA1 $NODE_ENV $LOG_LEVEL $CAPTURE_LOGS $LOGENTRIES_TOKEN $API_VERSION $AWS_REGION $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY $AUTH_DOMAIN $AUTH_SECRET $VALID_ISSUERS $DB_MASTER_URL $MEMBER_SERVICE_ENDPOINT $IDENTITY_SERVICE_ENDPOINT $BUS_API_URL $MESSAGE_SERVICE_URL $SYSTEM_USER_CLIENT_ID $SYSTEM_USER_CLIENT_SECRET $PROJECTS_ES_URL $PROJECTS_ES_INDEX_NAME $RABBITMQ_URL $DIRECT_PROJECT_SERVICE_ENDPOINT $FILE_SERVICE_ENDPOINT $CONNECT_PROJECTS_URL $SEGMENT_ANALYTICS_KEY "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID $KAFKA_URL $PORT $PORT $AWS_ECS_CLUSTER $AWS_REGION $NODE_ENV) } push_ecr_image(){ diff --git a/src/services/kafkaConsumer.js b/src/services/kafkaConsumer.js index 639deced..7e5d6471 100644 --- a/src/services/kafkaConsumer.js +++ b/src/services/kafkaConsumer.js @@ -11,9 +11,9 @@ export default async function startKafkaConsumer(handlers, app, logger) { // Read config and prepare Kafka options object const kafkaConfig = config.get('kafkaConfig'); - const options = {}; - if (kafkaConfig.has('hosts')) { - options.connectionString = kafkaConfig.get('hosts'); + const options = { groupId: kafkaConfig.get('groupId') }; + if (kafkaConfig.has('url')) { + options.connectionString = kafkaConfig.get('url'); } if (kafkaConfig.has('clientCert') && kafkaConfig.has('clientCertKey')) { options.ssl = { From 23bb264affbfb757fa6adb4aff8e85a5ee554127 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 19 Sep 2018 19:01:46 +0530 Subject: [PATCH 63/77] Fixed unit tests Fixed groupId env variable key --- config/custom-environment-variables.json | 2 +- src/events/busApi.js | 1 + src/routes/phases/update.spec.js | 65 ++++++------------------ 3 files changed, 18 insertions(+), 50 deletions(-) diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 17ee7bf4..eacbfe19 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -30,7 +30,7 @@ "minPoolSize": "DB_MIN_POOL_SIZE" }, "kafkaConfig": { - "groupdId": "KAFKA_GROUP_ID", + "groupId": "KAFKA_GROUP_ID", "url": "KAFKA_URL", "clientCert": "KAFKA_CLIENT_CERT", "clientCertKey": "KAFKA_CLIENT_CERT_KEY" diff --git a/src/events/busApi.js b/src/events/busApi.js index f30cbb5b..f35c4700 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -414,6 +414,7 @@ module.exports = (app, logger) => { events.forEach(event => createEvent(event, { projectId, phaseId, + projectUrl: connectProjectUrl(projectId), originalPhase: original, updatedPhase: updated, projectName: project.name, diff --git a/src/routes/phases/update.spec.js b/src/routes/phases/update.spec.js index ae9bd30c..85e8e44d 100644 --- a/src/routes/phases/update.spec.js +++ b/src/routes/phases/update.spec.js @@ -289,7 +289,7 @@ describe('Project Phases', () => { sandbox.restore(); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when spentBudget updated', (done) => { + it('should NOT send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when spentBudget updated', (done) => { request(server) .patch(`/v4/projects/${projectId}/phases/${phaseId}`) .set({ @@ -307,23 +307,16 @@ describe('Project Phases', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.calledTwice.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; + createEventSpy.calledOnce.should.be.true; - createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT); + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PAYMENT); done(); }); } }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when progress updated', (done) => { + it('should NOT send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when progress updated', (done) => { request(server) .patch(`/v4/projects/${projectId}/phases/${phaseId}`) .set({ @@ -341,15 +334,8 @@ describe('Project Phases', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.callCount.should.be.eql(3); - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS); + createEventSpy.callCount.should.be.eql(2); + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_PROGRESS); createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PROGRESS_MODIFIED); done(); }); @@ -357,7 +343,7 @@ describe('Project Phases', () => { }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when details updated', (done) => { + it('should NOT send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when details updated', (done) => { request(server) .patch(`/v4/projects/${projectId}/phases/${phaseId}`) .set({ @@ -377,22 +363,15 @@ describe('Project Phases', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.calledTwice.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_SCOPE); + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_UPDATE_SCOPE); done(); }); } }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when status updated', (done) => { + it('should NOT send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when status updated (completed)', (done) => { request(server) .patch(`/v4/projects/${projectId}/phases/${phaseId}`) .set({ @@ -410,22 +389,15 @@ describe('Project Phases', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.calledTwice.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; - createEventSpy.secondCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED); + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PHASE_TRANSITION_COMPLETED); done(); }); } }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when budget updated', (done) => { + it('should NOT send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when budget updated', (done) => { request(server) .patch(`/v4/projects/${projectId}/phases/${phaseId}`) .set({ @@ -443,14 +415,7 @@ describe('Project Phases', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.calledOnce.should.be.true; - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ - projectId, - projectName, - projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, - userId: 40051332, - initiatorUserId: 40051332, - })).should.be.true; + createEventSpy.notCalled.should.be.true; done(); }); } @@ -480,6 +445,8 @@ describe('Project Phases', () => { projectId, projectName, projectUrl: `https://local.topcoder-dev.com/projects/${projectId}`, + // originalPhase: sinon.match(originalPhase), + // updatedPhase: sinon.match(updatedPhase), userId: 40051332, initiatorUserId: 40051332, })).should.be.true; From 492a5c9315cd2a7bda2d63366236ee99be4a74a3 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 19 Sep 2018 19:32:47 +0530 Subject: [PATCH 64/77] fixing env variable for test cases --- src/services/kafkaConsumer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/kafkaConsumer.js b/src/services/kafkaConsumer.js index 7e5d6471..199e24a3 100644 --- a/src/services/kafkaConsumer.js +++ b/src/services/kafkaConsumer.js @@ -11,7 +11,10 @@ export default async function startKafkaConsumer(handlers, app, logger) { // Read config and prepare Kafka options object const kafkaConfig = config.get('kafkaConfig'); - const options = { groupId: kafkaConfig.get('groupId') }; + const options = {}; + if (kafkaConfig.has('groupId')) { + options.groupId = kafkaConfig.get('groupId'); + } if (kafkaConfig.has('url')) { options.connectionString = kafkaConfig.get('url'); } From 5cbd1f54dc62e36f13f0b32827034b1d1090e052 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 20 Sep 2018 13:03:59 +0530 Subject: [PATCH 65/77] trying fix for kafka connection error --- src/services/kafkaConsumer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/kafkaConsumer.js b/src/services/kafkaConsumer.js index 199e24a3..27df1104 100644 --- a/src/services/kafkaConsumer.js +++ b/src/services/kafkaConsumer.js @@ -19,9 +19,12 @@ export default async function startKafkaConsumer(handlers, app, logger) { options.connectionString = kafkaConfig.get('url'); } if (kafkaConfig.has('clientCert') && kafkaConfig.has('clientCertKey')) { + + const clientCert = kafkaConfig.get('clientCert').replace('\\n', '\n'); + const clientCertKey = kafkaConfig.has('clientCertKey').replace('\\n', '\n'); options.ssl = { - cert: kafkaConfig.get('clientCert'), - key: kafkaConfig.has('clientCertKey'), + cert: clientCert, + key: clientCertKey, }; } From 7e0c75c9725233f3198908c4afa5701b0fa811e8 Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Thu, 20 Sep 2018 13:18:57 +0530 Subject: [PATCH 66/77] lint-fix --- src/services/kafkaConsumer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/kafkaConsumer.js b/src/services/kafkaConsumer.js index 27df1104..be3e121c 100644 --- a/src/services/kafkaConsumer.js +++ b/src/services/kafkaConsumer.js @@ -19,7 +19,6 @@ export default async function startKafkaConsumer(handlers, app, logger) { options.connectionString = kafkaConfig.get('url'); } if (kafkaConfig.has('clientCert') && kafkaConfig.has('clientCertKey')) { - const clientCert = kafkaConfig.get('clientCert').replace('\\n', '\n'); const clientCertKey = kafkaConfig.has('clientCertKey').replace('\\n', '\n'); options.ssl = { From a8f8f54f64acd3d07dcd4c2e234b7facbcb50959 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 20 Sep 2018 14:03:35 +0530 Subject: [PATCH 67/77] fixed typo --- src/services/kafkaConsumer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/kafkaConsumer.js b/src/services/kafkaConsumer.js index 27df1104..22133821 100644 --- a/src/services/kafkaConsumer.js +++ b/src/services/kafkaConsumer.js @@ -21,7 +21,7 @@ export default async function startKafkaConsumer(handlers, app, logger) { if (kafkaConfig.has('clientCert') && kafkaConfig.has('clientCertKey')) { const clientCert = kafkaConfig.get('clientCert').replace('\\n', '\n'); - const clientCertKey = kafkaConfig.has('clientCertKey').replace('\\n', '\n'); + const clientCertKey = kafkaConfig.get('clientCertKey').replace('\\n', '\n'); options.ssl = { cert: clientCert, key: clientCertKey, From d81169ed726016d09d0d26e89cb1ea7d92814903 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 20 Sep 2018 14:59:40 +0530 Subject: [PATCH 68/77] Ignoring unmentioned keys from validation --- src/events/projects/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/projects/index.js b/src/events/projects/index.js index 505d7ddb..74b26b28 100644 --- a/src/events/projects/index.js +++ b/src/events/projects/index.js @@ -141,7 +141,7 @@ const payloadSchema = Joi.object().keys({ projectUrl: Joi.string().regex(REGEX.URL).required(), userId: Joi.number().integer().positive().required(), initiatorUserId: Joi.number().integer().positive().required(), -}).required(); +}).unknown(true).required(); /** * Updates project activity fields. throws exceptions in case of error From e67e265dfb3188767faa91e00c45c29497d92750 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 20 Sep 2018 15:07:21 +0530 Subject: [PATCH 69/77] changed fields to optional --- src/events/projects/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/events/projects/index.js b/src/events/projects/index.js index 74b26b28..34fe0834 100644 --- a/src/events/projects/index.js +++ b/src/events/projects/index.js @@ -137,8 +137,8 @@ const projectDeletedHandler = Promise.coroutine(function* (logger, msg, channel) const payloadSchema = Joi.object().keys({ projectId: Joi.number().integer().positive().required(), - projectName: Joi.string().required(), - projectUrl: Joi.string().regex(REGEX.URL).required(), + projectName: Joi.string().optional(), + projectUrl: Joi.string().regex(REGEX.URL).optional(), userId: Joi.number().integer().positive().required(), initiatorUserId: Joi.number().integer().positive().required(), }).unknown(true).required(); From 80c7897ee2c6512ee2f32880e8c4e121d27da0b4 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 28 Sep 2018 12:29:21 +0530 Subject: [PATCH 70/77] =?UTF-8?q?Separated=20the=20raising=20of=20project?= =?UTF-8?q?=20and=20phase=20events=20from=20timeline/milestone=20changes.?= =?UTF-8?q?=20Now=20we=20consume=20the=20timeline/milestone=20events=20in?= =?UTF-8?q?=20project=20service=20itself=20and=20then=20raise=20the=20appr?= =?UTF-8?q?opriate=20events.=20Another=20important=20change=20is=20that=20?= =?UTF-8?q?based=20on=20timeline/milestone=20changes,=20it=20updates=20the?= =?UTF-8?q?=20phase=E2=80=99s=20data=20e.g.=20it=20now=20updates=20the=20p?= =?UTF-8?q?hase=E2=80=99s=20progress=20and=20duration=20when=20a=20milesto?= =?UTF-8?q?ne=20is=20completed=20or=20milestone=20duration=20is=20changed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 2 +- src/constants.js | 5 +- src/events/busApi.js | 76 +++++++++++--------- src/events/index.js | 18 ++++- src/events/milestones/index.js | 100 +++++++++++++++++++++++++++ src/events/timelines/index.js | 94 +++++++++++++++++++++++++ src/models/milestone.js | 49 +++++++++++++ src/routes/milestones/create.spec.js | 4 +- src/routes/milestones/delete.spec.js | 6 +- src/routes/milestones/update.js | 16 +++-- src/routes/milestones/update.spec.js | 30 +++++--- src/routes/timelines/update.spec.js | 6 +- 12 files changed, 345 insertions(+), 61 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fec74ad4..459ec723 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -76,7 +76,7 @@ workflows: - test filters: branches: - only: ['dev', 'feature/timeline-milestone'] + only: ['dev'] - deployProd: requires: - test diff --git a/src/constants.js b/src/constants.js index c41703e8..b2dca636 100644 --- a/src/constants.js +++ b/src/constants.js @@ -101,11 +101,14 @@ export const BUS_API_EVENT = { // When milestone is added/deleted to/from the phase, // When milestone is updated for duration/startDate/endDate/status - TIMELINE_MODIFIED: 'notifications.connect.project.phase.timelineModified', + TIMELINE_ADJUSTED: 'notifications.connect.project.phase.timeline.adjusted', // When specification of a product is modified PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'notifications.connect.project.productSpecificationModified', + MILESTONE_ADDED: 'notifications.connect.project.phase.milestone.added', + MILESTONE_REMOVED: 'notifications.connect.project.phase.milestone.removed', + MILESTONE_UPDATED: 'notifications.connect.project.phase.milestone.updated', // When milestone is marked as active MILESTONE_TRANSITION_ACTIVE: 'notifications.connect.project.phase.milestone.transition.active', // When milestone is marked as completed diff --git a/src/events/busApi.js b/src/events/busApi.js index f35c4700..50ff0f61 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -474,10 +474,22 @@ module.exports = (app, logger) => { * @param {Object} original the original milestone * @param {Object} updated the updated milestone * @param {Object} project the project + * @param {Object} timeline the updated timeline * @returns {Promise} void */ - function sendMilestoneNotification(req, original, updated, project) { + function sendMilestoneNotification(req, original, updated, project, timeline) { logger.debug('sendMilestoneNotification', original, updated); + // throw generic milestone updated bus api event + createEvent(BUS_API_EVENT.MILESTONE_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + timeline, + originalMilestone: original, + updatedMilestone: updated, + userId: req.authUser.userId, + initiatorUserId: req.authUser.userId, + }, logger); // Send transition events if (original.status !== updated.status) { let event; @@ -492,8 +504,7 @@ module.exports = (app, logger) => { projectId: project.id, projectName: project.name, projectUrl: connectProjectUrl(project.id), - timelineId: req.timeline.id, - timelineName: req.timeline.name, + timeline, originalMilestone: original, updatedMilestone: updated, userId: req.authUser.userId, @@ -510,8 +521,7 @@ module.exports = (app, logger) => { projectId: project.id, projectName: project.name, projectUrl: connectProjectUrl(project.id), - timelineId: req.timeline.id, - timelineName: req.timeline.name, + timeline, originalMilestone: original, updatedMilestone: updated, userId: req.authUser.userId, @@ -533,15 +543,16 @@ module.exports = (app, logger) => { }) .then((project) => { if (project) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + createEvent(BUS_API_EVENT.MILESTONE_ADDED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), + addedMilestone: created, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); } - sendMilestoneNotification(req, {}, created, project); + // sendMilestoneNotification(req, {}, created, project); }) .catch(err => null); // eslint-disable-line no-unused-vars }); @@ -551,60 +562,54 @@ module.exports = (app, logger) => { */ // eslint-disable-next-line no-unused-vars app.on(EVENT.ROUTING_KEY.MILESTONE_UPDATED, ({ req, original, updated, cascadedUpdates }) => { - logger.debug('receive MILESTONE_UPDATED event'); + logger.debug(`receive MILESTONE_UPDATED event for milestone ${original.id}`); const projectId = _.parseInt(req.params.projectId); + const timeline = _.omit(req.timeline.toJSON(), 'deletedAt', 'deletedBy'); models.Project.findOne({ where: { id: projectId }, }) - .then((project) => { - // send PROJECT_UPDATED Kafka message when one of the specified below properties changed - const watchProperties = ['startDate', 'endDate', 'duration', 'details', 'status', 'order']; - if (!_.isEqual(_.pick(original, watchProperties), - _.pick(updated, watchProperties))) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { - projectId, - projectName: project.name, - projectUrl: connectProjectUrl(projectId), - userId: req.authUser.userId, - initiatorUserId: req.authUser.userId, - }, logger); - } - sendMilestoneNotification(req, original, updated, project); + .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), + sendMilestoneNotification(req, cascadedUpdate.original, cascadedUpdate.updated, project, timeline), ); } // if timeline is modified if (cascadedUpdates && cascadedUpdates.timeline) { - const timeline = cascadedUpdates.timeline; - // if endDate of the timeline is modified, raise TIMELINE_MODIFIED event - if (timeline.original.endDate !== timeline.updated.endDate) { + const cTimeline = cascadedUpdates.timeline; + // if endDate of the timeline is modified, raise TIMELINE_ADJUSTED event + if (cTimeline.original.endDate !== cTimeline.updated.endDate) { // Raise Timeline changed event - createEvent(BUS_API_EVENT.TIMELINE_MODIFIED, { + createEvent(BUS_API_EVENT.TIMELINE_ADJUSTED, { projectId: project.id, projectName: project.name, projectUrl: connectProjectUrl(project.id), - original: timeline.original, - updated: timeline.updated, + originalTimeline: cTimeline.original, + updatedTimeline: cTimeline.updated, 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 }); /** * MILESTONE_REMOVED. */ - app.on(EVENT.ROUTING_KEY.MILESTONE_REMOVED, ({ req }) => { + app.on(EVENT.ROUTING_KEY.MILESTONE_REMOVED, ({ req, deleted }) => { logger.debug('receive MILESTONE_REMOVED event'); // req.params.projectId is set by validateTimelineIdParam middleware const projectId = _.parseInt(req.params.projectId); @@ -614,10 +619,11 @@ module.exports = (app, logger) => { }) .then((project) => { if (project) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + createEvent(BUS_API_EVENT.MILESTONE_REMOVED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), + removedMilestone: deleted, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); @@ -639,10 +645,12 @@ module.exports = (app, logger) => { }) .then((project) => { if (project) { - createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + createEvent(BUS_API_EVENT.TIMELINE_ADJUSTED, { projectId, projectName: project.name, projectUrl: connectProjectUrl(projectId), + originalTimeline: original, + updatedTimeline: updated, userId: req.authUser.userId, initiatorUserId: req.authUser.userId, }, logger); diff --git a/src/events/index.js b/src/events/index.js index 38a1ed00..23a3f037 100644 --- a/src/events/index.js +++ b/src/events/index.js @@ -10,8 +10,18 @@ import { projectPhaseAddedHandler, projectPhaseRemovedHandler, projectPhaseUpdatedHandler } from './projectPhases'; import { phaseProductAddedHandler, phaseProductRemovedHandler, phaseProductUpdatedHandler } from './phaseProducts'; -import { timelineAddedHandler, timelineUpdatedHandler, timelineRemovedHandler } from './timelines'; -import { milestoneAddedHandler, milestoneUpdatedHandler, milestoneRemovedHandler } from './milestones'; +import { + timelineAddedHandler, + timelineUpdatedHandler, + timelineRemovedHandler, + timelineAdjustedKafkaHandler, +} from './timelines'; +import { + milestoneAddedHandler, + milestoneUpdatedHandler, + milestoneRemovedHandler, + milestoneUpdatedKafkaHandler, +} from './milestones'; export const rabbitHandlers = { 'project.initial': projectCreatedHandler, @@ -56,4 +66,8 @@ export const kafkaHandlers = { [BUS_API_EVENT.TOPIC_UPDATED]: projectUpdatedKafkaHandler, [BUS_API_EVENT.POST_CREATED]: projectUpdatedKafkaHandler, [BUS_API_EVENT.POST_UPDATED]: projectUpdatedKafkaHandler, + + // Events coming from timeline/milestones (considering it as a separate module/service in future) + [BUS_API_EVENT.MILESTONE_TRANSITION_COMPLETED]: milestoneUpdatedKafkaHandler, + [BUS_API_EVENT.TIMELINE_ADJUSTED]: timelineAdjustedKafkaHandler, }; diff --git a/src/events/milestones/index.js b/src/events/milestones/index.js index 71fd0d6b..d8f884a9 100644 --- a/src/events/milestones/index.js +++ b/src/events/milestones/index.js @@ -3,8 +3,12 @@ */ import config from 'config'; import _ from 'lodash'; +import Joi from 'joi'; import Promise from 'bluebird'; import util from '../../util'; +// import { createEvent } from '../../services/busApi'; +import { EVENT, TIMELINE_REFERENCES, MILESTONE_STATUS, REGEX } from '../../constants'; +import models from '../../models'; const ES_TIMELINE_INDEX = config.get('elasticsearchConfig.timelineIndexName'); const ES_TIMELINE_TYPE = config.get('elasticsearchConfig.timelineDocType'); @@ -154,9 +158,105 @@ const milestoneRemovedHandler = Promise.coroutine(function* (logger, msg, channe } }); +/** + * Kafka event handlers + */ + +const payloadSchema = Joi.object().keys({ + projectId: Joi.number().integer().positive().required(), + projectName: Joi.string().optional(), + projectUrl: Joi.string().regex(REGEX.URL).optional(), + userId: Joi.number().integer().positive().required(), + initiatorUserId: Joi.number().integer().positive().required(), +}).unknown(true).required(); + +const findProjectPhaseProduct = function (logger, productId, raw = true) { // eslint-disable-line func-names + let product; + return models.PhaseProduct.findOne({ + where: { id: productId }, + raw, + }).then((_product) => { + logger.debug('_product', _product); + if (_product) { + product = _product; + const phaseId = product.phaseId; + const projectId = product.projectId; + return Promise.all([ + models.ProjectPhase.findOne({ + where: { id: phaseId, projectId }, + raw, + }), + models.Project.findOne({ + where: { id: projectId }, + raw, + }), + ]); + } + return Promise.reject('Unable to find product'); + }).then((projectAndPhase) => { + logger.debug('projectAndPhase', projectAndPhase); + if (projectAndPhase) { + const phase = projectAndPhase[0]; + const project = projectAndPhase[1]; + return Promise.resolve({ product, phase, project }); + } + return Promise.reject('Unable to find phase/project'); + }); +}; + +/** + * Raises the project plan modified event + * @param {Object} app Application object used to interact with RMQ service + * @param {String} topic Kafka topic + * @param {Object} payload Message payload + * @return {Promise} Promise + */ +async function milestoneUpdatedKafkaHandler(app, topic, payload) { + app.logger.info(`Handling Kafka event for ${topic}`); + // Validate payload + const result = Joi.validate(payload, payloadSchema); + if (result.error) { + throw new Error(result.error); + } + + const timeline = payload.timeline; + // process only if timeline is related to a product reference + if (timeline && timeline.reference === TIMELINE_REFERENCES.PRODUCT) { + const productId = timeline.referenceId; + const original = payload.originalMilestone; + const updated = payload.updatedMilestone; + app.logger.debug('Calling findProjectPhaseProduct'); + const { project, phase } = await findProjectPhaseProduct(app.logger, productId, false); + app.logger.debug('Successfully fetched project, phase and product'); + if (original.status !== updated.status) { + if (updated.status === MILESTONE_STATUS.COMPLETED) { + app.logger.debug('Found milestone status to be completed'); + app.logger.debug(`Duration: ${timeline.duration}`); + if (!isNaN(timeline.duration) && !isNaN(timeline.progress)) { + app.logger.debug(`Current phase progress ${phase.progress} and duration ${phase.duration}`); + const updatedPhase = await phase.update({ + progress: timeline.progress, + duration: timeline.duration, + }, ['progress', 'duration']); + app.logger.debug(`Updated phase progress ${timeline.progress} and duration ${timeline.duration}`); + app.logger.debug('Raising node event for PROJECT_PHASE_UPDATED'); + app.emit(EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED, { + req: { + params: { projectId: project.id, phaseId: phase.id }, + authUser: { userId: payload.userId }, + }, + original: phase, + updated: _.omit(updatedPhase.toJSON(), 'deletedAt', 'deletedBy'), + }); + } + } + } + } +} module.exports = { milestoneAddedHandler, milestoneRemovedHandler, milestoneUpdatedHandler, + milestoneUpdatedKafkaHandler, }; diff --git a/src/events/timelines/index.js b/src/events/timelines/index.js index 0de36410..39ed0636 100644 --- a/src/events/timelines/index.js +++ b/src/events/timelines/index.js @@ -2,14 +2,29 @@ * Event handlers for timeline create, update and delete */ import _ from 'lodash'; +import Joi from 'joi'; import Promise from 'bluebird'; import config from 'config'; import util from '../../util'; +import { BUS_API_EVENT, TIMELINE_REFERENCES, REGEX } from '../../constants'; +import models from '../../models'; +import { createEvent } from '../../services/busApi'; const ES_TIMELINE_INDEX = config.get('elasticsearchConfig.timelineIndexName'); const ES_TIMELINE_TYPE = config.get('elasticsearchConfig.timelineDocType'); const eClient = util.getElasticSearchClient(); + +/** + * Builds the connect project url for the given project id. + * + * @param {string|number} projectId the project id + * @returns {string} the connect project url + */ +function connectProjectUrl(projectId) { + return `${config.get('connectProjectsUrl')}${projectId}`; +} + /** * Handler for timeline creation event * @param {Object} logger logger to log along with trace id @@ -81,10 +96,89 @@ const timelineRemovedHandler = Promise.coroutine(function* (logger, msg, channel channel.nack(msg, false, !msg.fields.redelivered); } }); +/** + * Kafka event handlers + */ + +const payloadSchema = Joi.object().keys({ + projectId: Joi.number().integer().positive().required(), + projectName: Joi.string().optional(), + projectUrl: Joi.string().regex(REGEX.URL).optional(), + userId: Joi.number().integer().positive().required(), + initiatorUserId: Joi.number().integer().positive().required(), +}).unknown(true).required(); +const findProjectPhaseProduct = function (logger, productId) { // eslint-disable-line func-names + let product; + return models.PhaseProduct.findOne({ + where: { id: productId }, + raw: true, + }).then((_product) => { + logger.debug('_product', _product); + if (_product) { + product = _product; + const phaseId = product.phaseId; + const projectId = product.projectId; + return Promise.all([ + models.ProjectPhase.findOne({ + where: { id: phaseId, projectId }, + raw: true, + }), + models.Project.findOne({ + where: { id: projectId }, + raw: true, + }), + ]); + } + return Promise.reject('Unable to find product'); + }).then((projectAndPhase) => { + logger.debug('projectAndPhase', projectAndPhase); + if (projectAndPhase) { + const phase = projectAndPhase[0]; + const project = projectAndPhase[1]; + return Promise.resolve({ product, phase, project }); + } + return Promise.reject('Unable to find phase/project'); + }); +}; + +/** + * Raises the project plan modified event + * @param {Object} app Application object used to interact with RMQ service + * @param {String} topic Kafka topic + * @param {Object} payload Message payload + * @return {Promise} Promise + */ +async function timelineAdjustedKafkaHandler(app, topic, payload) { + app.logger.debug(`Handling Kafka event for ${topic}`); + // Validate payload + const result = Joi.validate(payload, payloadSchema); + if (result.error) { + throw new Error(result.error); + } + + const timeline = payload.updatedTimeline; + // process only if timeline is related to a product reference + if (timeline && timeline.reference === TIMELINE_REFERENCES.PRODUCT) { + app.logger.debug('Found product timelin event '); + const productId = timeline.referenceId; + app.logger.debug('Calling findProjectPhaseProduct'); + const { project } = await findProjectPhaseProduct(app.logger, productId); + app.logger.debug('Successfully fetched project, phase and product'); + app.logger.debug('Raising BUS event for PROJECT_PLAN_UPDATED'); + createEvent(BUS_API_EVENT.PROJECT_PLAN_UPDATED, { + projectId: project.id, + projectName: project.name, + projectUrl: connectProjectUrl(project.id), + userId: payload.userId, + initiatorUserId: payload.userId, + }, app.logger); + } +} module.exports = { timelineAddedHandler, timelineUpdatedHandler, timelineRemovedHandler, + timelineAdjustedKafkaHandler, }; diff --git a/src/models/milestone.js b/src/models/milestone.js index 0f4bc4ec..53429883 100644 --- a/src/models/milestone.js +++ b/src/models/milestone.js @@ -1,3 +1,4 @@ +import moment from 'moment'; /* eslint-disable valid-jsdoc */ /** @@ -35,6 +36,54 @@ module.exports = (sequelize, DataTypes) => { updatedAt: 'updatedAt', createdAt: 'createdAt', deletedAt: 'deletedAt', + classMethods: { + /** + * Get total duration of the given timeline by summing up individual milestone durations + * @param timelineId the id of timeline + */ + getTimelineDuration(timelineId) { + console.log('getTimelineDuration'); + const where = { timelineId, hidden: false }; + return this.findAll({ + where, + order: [['order', 'asc']], + 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; + } else { + mDuration = moment.utc(m.completionDate).diff(moment.utc(m.startDate), 'days') + 1; + } + scheduledDuration += mDuration; + completedDuration += mDuration; + } else { + scheduledDuration += m.duration; + } + }); + console.log(`${completedDuration} completed out of ${scheduledDuration} duration`); + if (scheduledDuration > 0) { + progress = Math.round((completedDuration / scheduledDuration) * 100); + } + } + return Promise.resolve({ duration, progress }); + }); + }, + }, }); return Milestone; diff --git a/src/routes/milestones/create.spec.js b/src/routes/milestones/create.spec.js index bf652b28..ab1424ca 100644 --- a/src/routes/milestones/create.spec.js +++ b/src/routes/milestones/create.spec.js @@ -629,7 +629,7 @@ describe('CREATE milestone', () => { sandbox.restore(); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone created', (done) => { + it('should send message BUS_API_EVENT.TIMELINE_ADJUSTED when milestone created', (done) => { request(server) .post('/v4/timelines/1/milestones') .set({ @@ -644,7 +644,7 @@ describe('CREATE milestone', () => { } else { testUtil.wait(() => { createEventSpy.calledOnce.should.be.true; - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + createEventSpy.calledWith(BUS_API_EVENT.MILESTONE_ADDED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', diff --git a/src/routes/milestones/delete.spec.js b/src/routes/milestones/delete.spec.js index ee9c41e3..c756b7b0 100644 --- a/src/routes/milestones/delete.spec.js +++ b/src/routes/milestones/delete.spec.js @@ -373,8 +373,8 @@ describe('DELETE milestone', () => { }); // not testing fields separately as startDate is required parameter, - // thus PROJECT_PLAN_UPDATED will be always sent - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone removed', (done) => { + // thus TIMELINE_ADJUSTED will be always sent + it('should send message BUS_API_EVENT.TIMELINE_ADJUSTED when milestone removed', (done) => { request(server) .delete('/v4/timelines/1/milestones/1') .set({ @@ -387,7 +387,7 @@ describe('DELETE milestone', () => { } else { testUtil.wait(() => { createEventSpy.calledOnce.should.be.true; - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + createEventSpy.calledWith(BUS_API_EVENT.MILESTONE_REMOVED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', diff --git a/src/routes/milestones/update.js b/src/routes/milestones/update.js index 90e179d5..98a5cdf1 100644 --- a/src/routes/milestones/update.js +++ b/src/routes/milestones/update.js @@ -55,7 +55,7 @@ function updateComingMilestones(origMilestone, updMilestone) { } // Calculate the endDate, and update it if different - const endDate = moment.utc(startDate).add(milestone.duration - 1, 'days').toDate(); + const endDate = moment.utc(milestone.startDate).add(milestone.duration - 1, 'days').toDate(); if (!_.isEqual(milestone.endDate, endDate)) { milestone.endDate = endDate; milestone.updatedBy = updMilestone.updatedBy; @@ -69,10 +69,13 @@ function updateComingMilestones(origMilestone, updMilestone) { firstMilestoneFound = true; } - // Set the next startDate value to the next day after completionDate if present or the endDate - startDate = moment.utc(milestone.completionDate - ? milestone.completionDate - : milestone.endDate).add(1, 'days').toDate(); + // if milestone is not hidden, update the startDate for the next milestone, otherwise keep the same startDate for next milestone + if (!milestone.hidden) { + // Set the next startDate value to the next day after completionDate if present or the endDate + startDate = moment.utc(milestone.completionDate + ? milestone.completionDate + : milestone.endDate).add(1, 'days').toDate(); + } return milestone.save(); }); @@ -174,6 +177,7 @@ module.exports = [ // if status has changed to be completed, set the compeltionDate if not provided if (entityToUpdate.status === MILESTONE_STATUS.COMPLETED) { entityToUpdate.completionDate = entityToUpdate.completionDate ? entityToUpdate.completionDate : today; + entityToUpdate.duration = entityToUpdate.completionDate.diff(entityToUpdate.actualStartDate, 'days') + 1; } // if status has changed to be active, set the startDate to today if (entityToUpdate.status === MILESTONE_STATUS.ACTIVE) { @@ -198,6 +202,7 @@ module.exports = [ // if completionDate has changed if (!statusChanged && completionDateChanged) { + entityToUpdate.duration = entityToUpdate.completionDate.diff(entityToUpdate.actualStartDate, 'days') + 1; entityToUpdate.status = MILESTONE_STATUS.COMPLETED; } @@ -253,6 +258,7 @@ module.exports = [ const needToCascade = !_.isEqual(original.completionDate, updated.completionDate) // completion date changed || original.duration !== updated.duration // duration changed || original.actualStartDate !== updated.actualStartDate; // actual start date updated + req.log.debug('needToCascade', needToCascade); // Update dates of the other milestones only if cascade updates needed if (needToCascade) { return updateComingMilestones(original, updated) diff --git a/src/routes/milestones/update.spec.js b/src/routes/milestones/update.spec.js index 6286c5d4..ad591287 100644 --- a/src/routes/milestones/update.spec.js +++ b/src/routes/milestones/update.spec.js @@ -1102,7 +1102,7 @@ describe('UPDATE Milestone', () => { sandbox.restore(); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone duration updated', (done) => { + it('should send message BUS_API_EVENT.TIMELINE_ADJUSTED when milestone duration updated', (done) => { request(server) .patch('/v4/timelines/1/milestones/1') .set({ @@ -1119,22 +1119,25 @@ describe('UPDATE Milestone', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.calledTwice.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + // 5 milestones in total, so it would trigger 5 events + // 4 MILESTONE_UPDATED events are for 4 non deleted milestones + // 1 TIMELINE_ADJUSTED event, because timeline's end date updated + createEventSpy.callCount.should.be.eql(5); + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MILESTONE_UPDATED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', userId: 40051332, initiatorUserId: 40051332, })).should.be.true; - createEventSpy.secondCall.calledWith(BUS_API_EVENT.TIMELINE_MODIFIED); + createEventSpy.lastCall.calledWith(BUS_API_EVENT.TIMELINE_ADJUSTED); done(); }); } }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone status updated', (done) => { + it('should send message BUS_API_EVENT.MILESTONE_UPDATED when milestone status updated', (done) => { request(server) .patch('/v4/timelines/1/milestones/1') .set({ @@ -1152,7 +1155,7 @@ describe('UPDATE Milestone', () => { } else { testUtil.wait(() => { createEventSpy.calledOnce.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MILESTONE_UPDATED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', @@ -1165,7 +1168,7 @@ describe('UPDATE Milestone', () => { }); }); - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone order updated', (done) => { + it('should ONLY send message BUS_API_EVENT.MILESTONE_UPDATED when milestone order updated', (done) => { request(server) .patch('/v4/timelines/1/milestones/1') .set({ @@ -1183,7 +1186,7 @@ describe('UPDATE Milestone', () => { } else { testUtil.wait(() => { createEventSpy.calledOnce.should.be.true; - createEventSpy.firstCall.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MILESTONE_UPDATED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', @@ -1196,7 +1199,7 @@ describe('UPDATE Milestone', () => { }); }); - it('should not send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when milestone plannedText updated', (done) => { + it('should ONLY send message BUS_API_EVENT.MILESTONE_UPDATED when milestone plannedText updated', (done) => { request(server) .patch('/v4/timelines/1/milestones/1') .set({ @@ -1213,7 +1216,14 @@ describe('UPDATE Milestone', () => { done(err); } else { testUtil.wait(() => { - createEventSpy.notCalled.should.be.true; + createEventSpy.calledOnce.should.be.true; + createEventSpy.firstCall.calledWith(BUS_API_EVENT.MILESTONE_UPDATED, sinon.match({ + projectId: 1, + projectName: 'test1', + projectUrl: 'https://local.topcoder-dev.com/projects/1', + userId: 40051332, + initiatorUserId: 40051332, + })).should.be.true; done(); }); } diff --git a/src/routes/timelines/update.spec.js b/src/routes/timelines/update.spec.js index 4cb811a9..72fffcb3 100644 --- a/src/routes/timelines/update.spec.js +++ b/src/routes/timelines/update.spec.js @@ -648,8 +648,8 @@ describe('UPDATE timeline', () => { }); // not testing fields separately as startDate is required parameter, - // thus PROJECT_PLAN_UPDATED will be always sent - it('should send message BUS_API_EVENT.PROJECT_PLAN_UPDATED when timeline updated', (done) => { + // thus TIMELINE_ADJUSTED will be always sent + it('should send message BUS_API_EVENT.TIMELINE_ADJUSTED when timeline updated', (done) => { request(server) .patch('/v4/timelines/1') .set({ @@ -663,7 +663,7 @@ describe('UPDATE timeline', () => { } else { testUtil.wait(() => { createEventSpy.calledOnce.should.be.true; - createEventSpy.calledWith(BUS_API_EVENT.PROJECT_PLAN_UPDATED, sinon.match({ + createEventSpy.calledWith(BUS_API_EVENT.TIMELINE_ADJUSTED, sinon.match({ projectId: 1, projectName: 'test1', projectUrl: 'https://local.topcoder-dev.com/projects/1', From b06e8a558a251c079f2ce4a8fd1fe3c5e2295274 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 28 Sep 2018 12:52:10 +0530 Subject: [PATCH 71/77] config based file upload in dev env --- config/custom-environment-variables.json | 1 + config/default.json | 1 + deploy.sh | 7 ++++++- src/routes/attachments/create.js | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index eacbfe19..59814a2c 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -4,6 +4,7 @@ "logLevel": "LOG_LEVEL", "version": "APP_VERSION", "captureLogs": "CAPTURE_LOGS", + "enableFileUpload": "ENABLE_FILE_UPLOAD", "logentriesToken": "LOGENTRIES_TOKEN", "elasticsearchConfig": { "host": "PROJECTS_ES_URL", diff --git a/config/default.json b/config/default.json index 79440586..da84855b 100644 --- a/config/default.json +++ b/config/default.json @@ -4,6 +4,7 @@ "logLevel": "info", "version": "v4", "captureLogs": "false", + "enableFileUpload": "true", "logentriesToken": "", "rabbitmqURL": "", "pubsubQueueName": "project.service", diff --git a/deploy.sh b/deploy.sh index 80bc15c0..40d77e74 100755 --- a/deploy.sh +++ b/deploy.sh @@ -61,6 +61,10 @@ make_task_def(){ "name": "NODE_ENV", "value": "%s" }, + { + "name": "ENABLE_FILE_UPLOAD", + "value": "%s" + }, { "name": "LOG_LEVEL", "value": "%s" @@ -236,6 +240,7 @@ make_task_def(){ fi echo "NODE_ENV" echo $NODE_ENV + ENABLE_FILE_UPLOAD=$(eval "echo \$${ENV}_ENABLE_FILE_UPLOAD") AUTH0_URL=$(eval "echo \$${ENV}_AUTH0_URL") AUTH0_AUDIENCE=$(eval "echo \$${ENV}_AUTH0_AUDIENCE") @@ -248,7 +253,7 @@ make_task_def(){ KAFKA_URL=$(eval "echo \$${ENV}_KAFKA_URL") - task_def=$(printf "$task_template" $family $ACCOUNT_ID $AWS_ECS_CONTAINER_NAME $ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $CIRCLE_SHA1 $NODE_ENV $LOG_LEVEL $CAPTURE_LOGS $LOGENTRIES_TOKEN $API_VERSION $AWS_REGION $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY $AUTH_DOMAIN $AUTH_SECRET $VALID_ISSUERS $DB_MASTER_URL $MEMBER_SERVICE_ENDPOINT $IDENTITY_SERVICE_ENDPOINT $BUS_API_URL $MESSAGE_SERVICE_URL $SYSTEM_USER_CLIENT_ID $SYSTEM_USER_CLIENT_SECRET $PROJECTS_ES_URL $PROJECTS_ES_INDEX_NAME $RABBITMQ_URL $DIRECT_PROJECT_SERVICE_ENDPOINT $FILE_SERVICE_ENDPOINT $CONNECT_PROJECTS_URL $SEGMENT_ANALYTICS_KEY "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID $KAFKA_URL $PORT $PORT $AWS_ECS_CLUSTER $AWS_REGION $NODE_ENV) + task_def=$(printf "$task_template" $family $ACCOUNT_ID $AWS_ECS_CONTAINER_NAME $ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $CIRCLE_SHA1 $NODE_ENV $ENABLE_FILE_UPLOAD $LOG_LEVEL $CAPTURE_LOGS $LOGENTRIES_TOKEN $API_VERSION $AWS_REGION $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY $AUTH_DOMAIN $AUTH_SECRET $VALID_ISSUERS $DB_MASTER_URL $MEMBER_SERVICE_ENDPOINT $IDENTITY_SERVICE_ENDPOINT $BUS_API_URL $MESSAGE_SERVICE_URL $SYSTEM_USER_CLIENT_ID $SYSTEM_USER_CLIENT_SECRET $PROJECTS_ES_URL $PROJECTS_ES_INDEX_NAME $RABBITMQ_URL $DIRECT_PROJECT_SERVICE_ENDPOINT $FILE_SERVICE_ENDPOINT $CONNECT_PROJECTS_URL $SEGMENT_ANALYTICS_KEY "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID $KAFKA_URL $PORT $PORT $AWS_ECS_CLUSTER $AWS_REGION $NODE_ENV) } push_ecr_image(){ diff --git a/src/routes/attachments/create.js b/src/routes/attachments/create.js index 31fc1db8..5aa5ed2f 100644 --- a/src/routes/attachments/create.js +++ b/src/routes/attachments/create.js @@ -67,7 +67,7 @@ module.exports = [ } const fileTransferPromise = new Promise((accept, reject) => { - if (process.env.NODE_ENV !== 'development') { + if (process.env.NODE_ENV !== 'development' || config.get('enableFileUpload') === 'true') { // get pre-signed Url req.log.debug('requesting presigned Url'); httpClient.post(`${fileServiceUrl}uploadurl/`, { From bcdeb5689ed3eb4ddc20d3269983d2c7731ee8b6 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 28 Sep 2018 14:21:55 +0530 Subject: [PATCH 72/77] =?UTF-8?q?Updated=20kafka=20topics=20names,=20mostl?= =?UTF-8?q?y=20for=20new=20topics=20in=20this=20release.=20However,=20upda?= =?UTF-8?q?ted=20a=20few=20old=20ones=20too=20because=20they=20were=20eith?= =?UTF-8?q?er=20using=20camel=20case=20names=20or=20something=20else=20tha?= =?UTF-8?q?t=20has=20to=20be=20changed=20even=20if=20we=20don=E2=80=99t=20?= =?UTF-8?q?change=20the=20name=20as=20per=20new=20standards.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/constants.js b/src/constants.js index b2dca636..f8568d9d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -88,33 +88,33 @@ export const BUS_API_EVENT = { PROJECT_LINK_CREATED: 'notifications.connect.project.linkCreated', PROJECT_FILE_UPLOADED: 'notifications.connect.project.fileUploaded', PROJECT_SPECIFICATION_MODIFIED: 'notifications.connect.project.specificationModified', - PROJECT_PROGRESS_MODIFIED: 'notifications.connect.project.progressModified', + PROJECT_PROGRESS_MODIFIED: 'connect.action.project.updated.progress', PROJECT_FILES_UPDATED: 'notifications.connect.project.files.updated', PROJECT_TEAM_UPDATED: 'notifications.connect.project.team.updated', // When phase is added/updated/deleted from the project, // When product is added/deleted from a phase // When product is updated on any field other than specification - PROJECT_PLAN_UPDATED: 'notifications.connect.project.plan.updated', + PROJECT_PLAN_UPDATED: 'connect.action.project.plan.updated', - PROJECT_PLAN_READY: 'notifications.connect.project.planReady', + PROJECT_PLAN_READY: 'connect.action.project.plan.ready', // When milestone is added/deleted to/from the phase, // When milestone is updated for duration/startDate/endDate/status - TIMELINE_ADJUSTED: 'notifications.connect.project.phase.timeline.adjusted', + TIMELINE_ADJUSTED: 'connect.action.timeline.adjusted', // When specification of a product is modified - PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'notifications.connect.project.productSpecificationModified', + PROJECT_PRODUCT_SPECIFICATION_MODIFIED: 'connect.action.project.product.update.spec', - MILESTONE_ADDED: 'notifications.connect.project.phase.milestone.added', - MILESTONE_REMOVED: 'notifications.connect.project.phase.milestone.removed', - MILESTONE_UPDATED: 'notifications.connect.project.phase.milestone.updated', + MILESTONE_ADDED: 'connect.action.timeline.milestone.added', + MILESTONE_REMOVED: 'connect.action.timeline.milestone.removed', + MILESTONE_UPDATED: 'connect.action.timeline.milestone.updated', // When milestone is marked as active - MILESTONE_TRANSITION_ACTIVE: 'notifications.connect.project.phase.milestone.transition.active', + MILESTONE_TRANSITION_ACTIVE: 'connect.action.timeline.milestone.transition.active', // When milestone is marked as completed - MILESTONE_TRANSITION_COMPLETED: 'notifications.connect.project.phase.milestone.transition.completed', + MILESTONE_TRANSITION_COMPLETED: 'connect.action.timeline.milestone.transition.completed', // When milestone is waiting for customers's input - MILESTONE_WAITING_CUSTOMER: 'notifications.connect.project.phase.milestone.waiting.customer', + MILESTONE_WAITING_CUSTOMER: 'connect.action.timeline.milestone.waiting.customer', // TC Message Service events TOPIC_CREATED: 'notifications.connect.project.topic.created', From 25e4b653f9e6897449f46f1e845e8f0698243362 Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Fri, 28 Sep 2018 15:55:10 +0530 Subject: [PATCH 73/77] Increase memory and cpu for task --- deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index 40d77e74..74749280 100755 --- a/deploy.sh +++ b/deploy.sh @@ -54,8 +54,8 @@ make_task_def(){ "name": "%s", "image": "%s.dkr.ecr.%s.amazonaws.com/%s:%s", "essential": true, - "memory": 200, - "cpu": 10, + "memory": 1000, + "cpu": 100, "environment": [ { "name": "NODE_ENV", From 860b6ef565b6a24765bed88852d8b913d0f6a8b3 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Sat, 29 Sep 2018 11:21:51 +0530 Subject: [PATCH 74/77] =?UTF-8?q?We=20don=E2=80=99t=20need=20to=20set=20do?= =?UTF-8?q?wnload=20URL=20any=20more=20per=20attachment=20because=20we=20c?= =?UTF-8?q?alculate=20the=20download=20URL=20at=20run=20time=20to=20avoid?= =?UTF-8?q?=20expiration=20of=20the=20signed=20url.=20It=20would=20also=20?= =?UTF-8?q?fix=20the=20issue=20in=20performance=20because=20now=20it=20won?= =?UTF-8?q?=E2=80=99t=20make=20extra=20calls=20to=20fetch=20download=20url?= =?UTF-8?q?s=20and=20also=20fixed=20the=20issue=20where=20we=20are=20unabl?= =?UTF-8?q?e=20to=20load=20project=20with=20many=20attachments.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/util.js b/src/util.js index 34e12c7d..906f1389 100644 --- a/src/util.js +++ b/src/util.js @@ -248,26 +248,9 @@ _.assignIn(util, { return models.ProjectAttachment.getActiveProjectAttachments(projectId) .then((_attachments) => { // if attachments were requested - if (attachments) { + if (_attachments) { attachments = _attachments; - } else { - return attachments; } - // TODO consider using redis to cache attachments urls - const promises = []; - _.each(attachments, (a) => { - promises.push(util.getFileDownloadUrl(req, a.filePath)); - }); - return Promise.all(promises); - }) - .then((result) => { - // result is an array of 'tuples' => [[path, url], [path,url]] - // convert it to a map for easy lookup - const urls = _.fromPairs(result); - _.each(attachments, (at) => { - const a = at; - a.downloadUrl = urls[a.filePath]; - }); return attachments; }); }, From 9913faec8205c5488ff90947f1b37e31aaefd1eb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Sat, 29 Sep 2018 11:37:14 +0530 Subject: [PATCH 75/77] fixed unit test for downloadUrl --- src/routes/projects/get.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/projects/get.spec.js b/src/routes/projects/get.spec.js index 08fd3bbf..af1b4e30 100644 --- a/src/routes/projects/get.spec.js +++ b/src/routes/projects/get.spec.js @@ -204,7 +204,8 @@ describe('GET Project', () => { spy.should.have.been.calledOnce; resJson.attachments.should.have.lengthOf(1); resJson.attachments[0].filePath.should.equal(attachment.filePath); - resJson.attachments[0].downloadUrl.should.exist; + // downloadUrl no more needed + // resJson.attachments[0].downloadUrl.should.exist; done(); } }); From ebf91989d89b2052a5a30e5a52c2f353bab87578 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 1 Oct 2018 11:05:54 +0530 Subject: [PATCH 76/77] increasing cpu and memory allocate to the container --- deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index 74749280..1d4ea007 100755 --- a/deploy.sh +++ b/deploy.sh @@ -54,8 +54,8 @@ make_task_def(){ "name": "%s", "image": "%s.dkr.ecr.%s.amazonaws.com/%s:%s", "essential": true, - "memory": 1000, - "cpu": 100, + "memory": 1536, + "cpu": 768, "environment": [ { "name": "NODE_ENV", From ddffacd613f1c7473aaeae2487a06022b4d50c1e Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Mon, 1 Oct 2018 12:01:21 +0530 Subject: [PATCH 77/77] Corrected Naming convention for kafka topics --- src/constants.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constants.js b/src/constants.js index f8568d9d..7f238600 100644 --- a/src/constants.js +++ b/src/constants.js @@ -64,7 +64,7 @@ export const EVENT = { export const BUS_API_EVENT = { PROJECT_CREATED: 'notifications.connect.project.created', - PROJECT_UPDATED: 'notifications.connect.project.updated', + PROJECT_UPDATED: 'connect.action.project.updated', PROJECT_SUBMITTED_FOR_REVIEW: 'notifications.connect.project.submittedForReview', PROJECT_APPROVED: 'notifications.connect.project.approved', PROJECT_PAUSED: 'notifications.connect.project.paused', @@ -89,8 +89,8 @@ export const BUS_API_EVENT = { PROJECT_FILE_UPLOADED: 'notifications.connect.project.fileUploaded', PROJECT_SPECIFICATION_MODIFIED: 'notifications.connect.project.specificationModified', PROJECT_PROGRESS_MODIFIED: 'connect.action.project.updated.progress', - PROJECT_FILES_UPDATED: 'notifications.connect.project.files.updated', - PROJECT_TEAM_UPDATED: 'notifications.connect.project.team.updated', + PROJECT_FILES_UPDATED: 'connect.action.project.files.updated', + PROJECT_TEAM_UPDATED: 'connect.action.project.team.updated', // When phase is added/updated/deleted from the project, // When product is added/deleted from a phase