Skip to content

Commit f5d4c63

Browse files
elkhawajaheisbilirtasqatRishiRajSahu
authored
[HOTFIX][PROD] bulk update member phases for milestone management (#678)
* [DEV] Milestone Management (#657) * phase members * Adding support to store linking between copilots and phases (aka milestones in the new concept) Added cross ref table and CRUD api endpoints * trigger CI Build * fix: phase member test spec * update: phase member details * updated script to include sequence creation and removed 'public' schema * update: phase update will return members * update: use 100 for maxPhaseProductCount * allow update phase members with create-update phase * use transaction * fix: phase member tests * adding phase approval api * make phase validation dates optional * update phase status * update postman tests * fix: product delete sync with ES * fix: phase approval migration script Co-authored-by: eisbilir <[email protected]> Co-authored-by: Ahmad Alkhawaja <[email protected]> * fix: update phase members * add bulk delete api for project phase Co-authored-by: eisbilir <[email protected]> Co-authored-by: Ahmad Alkhawaja <[email protected]> Co-authored-by: Rishiraj Sahu <[email protected]>
1 parent 8f570bc commit f5d4c63

File tree

5 files changed

+403
-7
lines changed

5 files changed

+403
-7
lines changed

docs/Project API.postman_collection.json

+37-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"info": {
3-
"_postman_id": "52f34e21-5b0b-4eb0-99fa-cbd1ac7f215a",
3+
"_postman_id": "6418ac6e-a797-4e30-b4d3-a1dd0cdead22",
44
"name": "Project API",
55
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
66
},
@@ -4834,7 +4834,7 @@
48344834
"exec": [
48354835
"pm.test(\"Status code is 201\", function () {",
48364836
" pm.response.to.have.status(201);",
4837-
" pm.environment.set(\"phaseId\", pm.response.json().id);",
4837+
" pm.environment.set(\"phaseId-2\", pm.response.json().id);",
48384838
"});"
48394839
],
48404840
"type": "text/javascript"
@@ -4880,7 +4880,7 @@
48804880
"exec": [
48814881
"pm.test(\"Status code is 201\", function () {",
48824882
" pm.response.to.have.status(201);",
4883-
" pm.environment.set(\"phaseId\", pm.response.json().id);",
4883+
" pm.environment.set(\"phaseId-3\", pm.response.json().id);",
48844884
"});"
48854885
],
48864886
"type": "text/javascript"
@@ -4901,7 +4901,7 @@
49014901
],
49024902
"body": {
49034903
"mode": "raw",
4904-
"raw": "{\n\t\"name\": \"test project phase\",\n\t\"status\": \"active\",\n\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\"budget\": 20,\n\t\"details\": {\n\t\t\"aDetails\": \"a details\"\n\t},\n\t\"order\": 1,\n\t\"productTemplateId\": {{productTemplateId}}\n}"
4904+
"raw": "{\n\t\"name\": \"test project phase\",\n\t\"status\": \"active\",\n\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\"budget\": 20,\n\t\"details\": {\n\t\t\"aDetails\": \"a details\"\n\t},\n\t\"order\": 1,\n\t\"productTemplateId\": 2\n}"
49054905
},
49064906
"url": {
49074907
"raw": "{{api-url}}/projects/{{projectId}}/phases",
@@ -4926,7 +4926,7 @@
49264926
"exec": [
49274927
"pm.test(\"Status code is 201\", function () {",
49284928
" pm.response.to.have.status(201);",
4929-
" pm.environment.set(\"phaseId\", pm.response.json().id);",
4929+
" pm.environment.set(\"phaseId-4\", pm.response.json().id);",
49304930
"});"
49314931
],
49324932
"type": "text/javascript"
@@ -5355,6 +5355,38 @@
53555355
}
53565356
},
53575357
"response": []
5358+
},
5359+
{
5360+
"name": "Bulk Delete Phase",
5361+
"request": {
5362+
"method": "DELETE",
5363+
"header": [
5364+
{
5365+
"key": "Authorization",
5366+
"value": "Bearer {{jwt-token}}"
5367+
},
5368+
{
5369+
"key": "Content-Type",
5370+
"value": "application/json"
5371+
}
5372+
],
5373+
"body": {
5374+
"mode": "raw",
5375+
"raw": "{\r\n \"phaseIds\": [\r\n {{phaseId-2}},\r\n {{phaseId-3}},\r\n {{phaseId-4}}\r\n ]\r\n}"
5376+
},
5377+
"url": {
5378+
"raw": "{{api-url}}/projects/{{projectId}}/phases",
5379+
"host": [
5380+
"{{api-url}}"
5381+
],
5382+
"path": [
5383+
"projects",
5384+
"{{projectId}}",
5385+
"phases"
5386+
]
5387+
}
5388+
},
5389+
"response": []
53585390
}
53595391
]
53605392
},

src/routes/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ router.route('/v5/projects/metadata/productTemplates/:templateId(\\d+)')
167167

168168
router.route('/v5/projects/:projectId(\\d+)/phases')
169169
.get(require('./phases/list'))
170-
.post(require('./phases/create'));
170+
.post(require('./phases/create'))
171+
.delete(require('./phases/bulkDelete'));
171172

172173
router.route('/v5/projects/:projectId(\\d+)/phases/:phaseId(\\d+)')
173174
.get(require('./phases/get'))

src/routes/phaseMembers/update.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ module.exports = [
5151
req.log.debug('updated phase members', JSON.stringify(newPhaseMembers, null, 2));
5252
const updatedPhase = _.cloneDeep(phase);
5353
// emit event
54-
if (_.intersectionBy(phaseMembers, updatedPhaseMembers, 'id').length !== updatedPhaseMembers.length) {
54+
if (!_.isEqual(_.sortBy(phaseMembers, 'id'), _.sortBy(updatedPhaseMembers, 'id'))) {
5555
util.sendResourceToKafkaBus(
5656
req,
5757
EVENT.ROUTING_KEY.PROJECT_PHASE_UPDATED,

src/routes/phases/bulkDelete.js

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import validate from 'express-validation';
2+
import _ from 'lodash';
3+
import { middleware as tcMiddleware } from 'tc-core-library-js';
4+
import Joi from 'joi';
5+
import models from '../../models';
6+
import util from '../../util';
7+
import { EVENT, RESOURCES } from '../../constants';
8+
9+
const permissions = tcMiddleware.permissions;
10+
11+
const bulkDeletePhaseValidation = {
12+
body: Joi.object().keys({
13+
phaseIds: Joi.array().items(Joi.number().integer()).required(),
14+
}).required(),
15+
};
16+
17+
module.exports = [
18+
// validate request payload
19+
validate(bulkDeletePhaseValidation),
20+
// check permission
21+
permissions('project.deleteProjectPhase'),
22+
23+
(req, res, next) => {
24+
const data = req.body;
25+
const projectId = _.parseInt(req.params.projectId);
26+
27+
models.sequelize.transaction(transaction =>
28+
// soft delete the record
29+
models.ProjectPhase.findAll({
30+
where: {
31+
id: data.phaseIds,
32+
projectId,
33+
deletedAt: { $eq: null },
34+
},
35+
raw: true,
36+
transaction,
37+
}).then((phases) => {
38+
const notFoundPhases = _.differenceWith(data.phaseIds, phases, (a, b) => a === b.id);
39+
if (!_.isEmpty(notFoundPhases)) {
40+
// handle 404
41+
const err = new Error('no active project phase found for project id ' +
42+
`${projectId} and phase ids ${notFoundPhases}`);
43+
err.status = 404;
44+
return Promise.reject(err);
45+
}
46+
return models.ProjectPhase.update({ deletedBy: req.authUser.userId }, {
47+
where: {
48+
id: data.phaseIds,
49+
projectId,
50+
},
51+
transaction,
52+
}).then(() =>
53+
models.ProjectPhase.destroy({
54+
where: {
55+
id: data.phaseIds,
56+
projectId,
57+
},
58+
transaction,
59+
}),
60+
);
61+
}))
62+
.then((deletedCount) => {
63+
const result = {
64+
id: data.phaseIds,
65+
projectId,
66+
};
67+
req.log.debug('deleted project phases', JSON.stringify(result, null, 2));
68+
if (deletedCount > 0) {
69+
util.sendResourceToKafkaBus(
70+
req,
71+
EVENT.ROUTING_KEY.PROJECT_PHASE_REMOVED,
72+
RESOURCES.PHASE,
73+
result,
74+
);
75+
}
76+
res.status(204).json({});
77+
}).catch(err => next(err));
78+
},
79+
];

0 commit comments

Comments
 (0)