Skip to content

Commit be9dd0e

Browse files
committed
winning submission from challenge 30094113 - Topcoder Project Service - Milestone status history
1 parent 20e71c9 commit be9dd0e

File tree

10 files changed

+298
-9
lines changed

10 files changed

+298
-9
lines changed

src/models/milestone.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
1+
import _ from 'lodash';
12
import moment from 'moment';
23
import models from '../models';
34
/* eslint-disable valid-jsdoc */
45

6+
/**
7+
* Populate and map milestone model with statusHistory
8+
* @param {Object} milestone the milestone
9+
* @returns {Promise} promise
10+
*/
11+
const mapWithStatusHistory = async (milestone) => {
12+
try {
13+
const statusHistory = await models.StatusHistory.findAll({
14+
where: {
15+
referenceId: milestone.id.toString(),
16+
reference: 'milestone',
17+
},
18+
order: [['createdAt', 'desc']],
19+
raw: true,
20+
});
21+
return _.merge(milestone, { dataValues: { statusHistory } });
22+
} catch (err) {
23+
return _.merge(milestone, { dataValues: { statusHistory: [] } });
24+
}
25+
};
26+
527
/**
628
* The Milestone model
729
*/
@@ -93,7 +115,7 @@ module.exports = (sequelize, DataTypes) => {
93115
updatedBy: milestone.updatedBy,
94116
}, {
95117
transaction: options.transaction,
96-
}),
118+
}).then(() => mapWithStatusHistory(milestone)),
97119

98120
afterUpdate: (milestone, options) => {
99121
if (milestone.changed().includes('status')) {
@@ -106,9 +128,18 @@ module.exports = (sequelize, DataTypes) => {
106128
updatedBy: milestone.updatedBy,
107129
}, {
108130
transaction: options.transaction,
109-
});
131+
}).then(() => mapWithStatusHistory(milestone));
132+
}
133+
return mapWithStatusHistory(milestone);
134+
},
135+
136+
afterFind: (milestone) => {
137+
if (!milestone) return Promise.resolve();
138+
139+
if (Array.isArray(milestone)) {
140+
return Promise.all(milestone.map(mapWithStatusHistory));
110141
}
111-
return Promise.resolve();
142+
return mapWithStatusHistory(milestone);
112143
},
113144
},
114145
});

src/routes/milestones/create.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,14 @@ describe('CREATE milestone', () => {
523523
should.not.exist(resJson.deletedBy);
524524
should.not.exist(resJson.deletedAt);
525525

526+
// validate statusHistory
527+
should.exist(resJson.statusHistory);
528+
resJson.statusHistory.should.be.an('array');
529+
resJson.statusHistory.forEach((statusHistory) => {
530+
statusHistory.reference.should.be.eql('milestone');
531+
statusHistory.referenceId.should.be.eql(`${resJson.id}`);
532+
});
533+
526534
// eslint-disable-next-line no-unused-expressions
527535
server.services.pubsub.publish.calledWith(EVENT.ROUTING_KEY.MILESTONE_ADDED).should.be.true;
528536

src/routes/milestones/get.spec.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ describe('GET milestone', () => {
133133
// Create milestones
134134
models.Milestone.bulkCreate([
135135
{
136+
id: 1,
136137
timelineId: 1,
137138
name: 'milestone 1',
138139
duration: 2,
@@ -155,6 +156,7 @@ describe('GET milestone', () => {
155156
updatedBy: 2,
156157
},
157158
{
159+
id: 2,
158160
timelineId: 1,
159161
name: 'milestone 2',
160162
duration: 3,
@@ -170,6 +172,7 @@ describe('GET milestone', () => {
170172
updatedBy: 3,
171173
},
172174
{
175+
id: 3,
173176
timelineId: 1,
174177
name: 'milestone 3',
175178
duration: 4,
@@ -187,7 +190,30 @@ describe('GET milestone', () => {
187190
deletedAt: '2018-05-04T00:00:00.000Z',
188191
},
189192
])
190-
.then(() => done());
193+
.then(() =>
194+
models.StatusHistory.bulkCreate([
195+
{
196+
reference: 'milestone',
197+
referenceId: '1',
198+
status: 'active',
199+
comment: 'comment',
200+
createdBy: 1,
201+
createdAt: '2018-05-15T00:00:00Z',
202+
updatedBy: 1,
203+
updatedAt: '2018-05-15T00:00:00Z',
204+
},
205+
{
206+
reference: 'milestone',
207+
referenceId: '1',
208+
status: 'active',
209+
comment: 'comment',
210+
createdBy: 1,
211+
createdAt: '2018-05-15T00:00:00Z',
212+
updatedBy: 1,
213+
updatedAt: '2018-05-15T00:00:00Z',
214+
},
215+
])
216+
.then(() => done()));
191217
});
192218
});
193219
});
@@ -301,6 +327,14 @@ describe('GET milestone', () => {
301327
should.not.exist(resJson.deletedBy);
302328
should.not.exist(resJson.deletedAt);
303329

330+
// validate statusHistory
331+
should.exist(resJson.statusHistory);
332+
resJson.statusHistory.should.be.an('array');
333+
resJson.statusHistory.forEach((statusHistory) => {
334+
statusHistory.reference.should.be.eql('milestone');
335+
statusHistory.referenceId.should.be.eql(`${resJson.id}`);
336+
});
337+
304338
done();
305339
});
306340
});

src/routes/milestones/list.spec.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import chai from 'chai';
55
import request from 'supertest';
66
import sleep from 'sleep';
77
import config from 'config';
8+
import _ from 'lodash';
89

910
import models from '../../models';
1011
import server from '../../app';
@@ -58,6 +59,16 @@ const milestones = [
5859
updatedBy: 2,
5960
createdAt: '2018-05-11T00:00:00.000Z',
6061
updatedAt: '2018-05-11T00:00:00.000Z',
62+
statusHistory: [{
63+
reference: 'milestone',
64+
referenceId: '1',
65+
status: 'active',
66+
comment: 'comment',
67+
createdBy: 1,
68+
createdAt: '2018-05-15T00:00:00Z',
69+
updatedBy: 1,
70+
updatedAt: '2018-05-15T00:00:00Z',
71+
}],
6172
},
6273
{
6374
id: 2,
@@ -76,6 +87,16 @@ const milestones = [
7687
updatedBy: 3,
7788
createdAt: '2018-05-11T00:00:00.000Z',
7889
updatedAt: '2018-05-11T00:00:00.000Z',
90+
statusHistory: [{
91+
reference: 'milestone',
92+
referenceId: '2',
93+
status: 'active',
94+
comment: 'comment',
95+
createdBy: 1,
96+
createdAt: '2018-05-15T00:00:00Z',
97+
updatedBy: 1,
98+
updatedAt: '2018-05-15T00:00:00Z',
99+
}],
79100
},
80101
];
81102

@@ -165,7 +186,10 @@ describe('LIST timelines', () => {
165186
.then(() =>
166187
// Create timelines and milestones
167188
models.Timeline.bulkCreate(timelines)
168-
.then(() => models.Milestone.bulkCreate(milestones)))
189+
.then(() => {
190+
const mappedMilstones = milestones.map(milestone => _.omit(milestone, ['statusHistory']));
191+
return models.Milestone.bulkCreate(mappedMilstones);
192+
}))
169193
.then(() => {
170194
// Index to ES
171195
timelines[0].milestones = milestones;
@@ -242,8 +266,15 @@ describe('LIST timelines', () => {
242266
const resJson = res.body.result.content;
243267
resJson.should.have.length(2);
244268

245-
resJson[0].should.be.eql(milestones[0]);
246-
resJson[1].should.be.eql(milestones[1]);
269+
resJson.forEach((milestone, index) => {
270+
milestone.statusHistory.should.be.an('array');
271+
milestone.statusHistory.forEach((statusHistory) => {
272+
statusHistory.reference.should.be.eql('milestone');
273+
statusHistory.referenceId.should.be.eql(`${milestone.id}`);
274+
});
275+
276+
milestone.should.be.eql(milestones[index]);
277+
});
247278

248279
done();
249280
});

src/routes/milestones/update.spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,28 @@ describe('UPDATE Milestone', () => {
252252
updatedAt: '2018-05-11T00:00:00.000Z',
253253
},
254254
])))
255+
.then(() => models.StatusHistory.bulkCreate([
256+
{
257+
reference: 'milestone',
258+
referenceId: '1',
259+
status: 'active',
260+
comment: 'comment',
261+
createdBy: 1,
262+
createdAt: '2018-05-15T00:00:00Z',
263+
updatedBy: 1,
264+
updatedAt: '2018-05-15T00:00:00Z',
265+
},
266+
{
267+
reference: 'milestone',
268+
referenceId: '2',
269+
status: 'active',
270+
comment: 'comment',
271+
createdBy: 1,
272+
createdAt: '2018-05-15T00:00:00Z',
273+
updatedBy: 1,
274+
updatedAt: '2018-05-15T00:00:00Z',
275+
},
276+
]))
255277
.then(() => done());
256278
});
257279
});
@@ -524,6 +546,14 @@ describe('UPDATE Milestone', () => {
524546
should.not.exist(resJson.deletedBy);
525547
should.not.exist(resJson.deletedAt);
526548

549+
// validate statusHistory
550+
should.exist(resJson.statusHistory);
551+
resJson.statusHistory.should.be.an('array');
552+
resJson.statusHistory.forEach((statusHistory) => {
553+
statusHistory.reference.should.be.eql('milestone');
554+
statusHistory.referenceId.should.be.eql(`${resJson.id}`);
555+
});
556+
527557
// eslint-disable-next-line no-unused-expressions
528558
server.services.pubsub.publish.calledWith(EVENT.ROUTING_KEY.MILESTONE_UPDATED).should.be.true;
529559

src/routes/timelines/create.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,14 @@ describe('CREATE timeline', () => {
529529
should.exist(milestone.updatedAt);
530530
should.not.exist(milestone.deletedBy);
531531
should.not.exist(milestone.deletedAt);
532+
533+
// validate statusHistory
534+
should.exist(milestone.statusHistory);
535+
milestone.statusHistory.should.be.an('array');
536+
milestone.statusHistory.forEach((statusHistory) => {
537+
statusHistory.reference.should.be.eql('milestone');
538+
statusHistory.referenceId.should.be.eql(`${milestone.id}`);
539+
});
532540
});
533541

534542
// eslint-disable-next-line no-unused-expressions

src/routes/timelines/get.spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ const milestones = [
5858
},
5959
];
6060

61+
const statusHistories = [
62+
{
63+
reference: 'milestone',
64+
referenceId: '1',
65+
status: 'active',
66+
comment: 'comment',
67+
createdBy: 1,
68+
createdAt: '2018-05-15T00:00:00Z',
69+
updatedBy: 1,
70+
updatedAt: '2018-05-15T00:00:00Z',
71+
},
72+
{
73+
reference: 'milestone',
74+
referenceId: '2',
75+
status: 'active',
76+
comment: 'comment',
77+
createdBy: 1,
78+
createdAt: '2018-05-15T00:00:00Z',
79+
updatedBy: 1,
80+
updatedAt: '2018-05-15T00:00:00Z',
81+
},
82+
];
83+
6184
describe('GET timeline', () => {
6285
before((done) => {
6386
testUtil.clearDb()
@@ -177,6 +200,7 @@ describe('GET timeline', () => {
177200
},
178201
]))
179202
.then(() => models.Milestone.bulkCreate(milestones))
203+
.then(() => models.StatusHistory.bulkCreate(statusHistories))
180204
.then(() => done());
181205
});
182206
});
@@ -262,6 +286,15 @@ describe('GET timeline', () => {
262286

263287
// Milestones
264288
resJson.milestones.should.have.length(2);
289+
resJson.milestones.forEach((milestone) => {
290+
// validate statusHistory
291+
should.exist(milestone.statusHistory);
292+
milestone.statusHistory.should.be.an('array');
293+
milestone.statusHistory.forEach((statusHistory) => {
294+
statusHistory.reference.should.be.eql('milestone');
295+
statusHistory.referenceId.should.be.eql(`${milestone.id}`);
296+
});
297+
});
265298

266299
done();
267300
});

src/routes/timelines/list.spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ const milestones = [
7575
updatedBy: 2,
7676
createdAt: '2018-05-11T00:00:00.000Z',
7777
updatedAt: '2018-05-11T00:00:00.000Z',
78+
statusHistory: [
79+
{
80+
reference: 'milestone',
81+
referenceId: '1',
82+
status: 'active',
83+
comment: 'comment',
84+
createdBy: 1,
85+
createdAt: '2018-05-15T00:00:00Z',
86+
updatedBy: 1,
87+
updatedAt: '2018-05-15T00:00:00Z',
88+
},
89+
],
7890
},
7991
{
8092
id: 2,
@@ -93,6 +105,18 @@ const milestones = [
93105
updatedBy: 3,
94106
createdAt: '2018-05-11T00:00:00.000Z',
95107
updatedAt: '2018-05-11T00:00:00.000Z',
108+
statusHistory: [
109+
{
110+
reference: 'milestone',
111+
referenceId: '2',
112+
status: 'active',
113+
comment: 'comment',
114+
createdBy: 1,
115+
createdAt: '2018-05-15T00:00:00Z',
116+
updatedBy: 1,
117+
updatedAt: '2018-05-15T00:00:00Z',
118+
},
119+
],
96120
},
97121
];
98122

@@ -276,6 +300,15 @@ describe('LIST timelines', () => {
276300

277301
// Milestones
278302
resJson[0].milestones.should.have.length(2);
303+
resJson[0].milestones.forEach((milestone) => {
304+
// validate statusHistory
305+
should.exist(milestone.statusHistory);
306+
milestone.statusHistory.should.be.an('array');
307+
milestone.statusHistory.forEach((statusHistory) => {
308+
statusHistory.reference.should.be.eql('milestone');
309+
statusHistory.referenceId.should.be.eql(`${milestone.id}`);
310+
});
311+
});
279312

280313
done();
281314
});

0 commit comments

Comments
 (0)