Skip to content

Commit 4fe4e6c

Browse files
committed
Add db query parameter to support getting timeline directly from database bypassing ElasticSearch.
1 parent 203892f commit 4fe4e6c

File tree

2 files changed

+140
-47
lines changed

2 files changed

+140
-47
lines changed

src/routes/timelines/get.js

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,47 @@ const schema = {
2020
params: {
2121
timelineId: Joi.number().integer().positive().required(),
2222
},
23+
query: {
24+
db: Joi.boolean().optional(),
25+
},
2326
};
2427

28+
// Load the milestones
29+
const loadMilestones = timeline =>
30+
timeline.getMilestones()
31+
.then((milestones) => {
32+
const loadedTimeline = _.omit(timeline.toJSON(), ['deletedAt', 'deletedBy']);
33+
loadedTimeline.milestones =
34+
_.map(milestones, milestone => _.omit(milestone.toJSON(), ['deletedAt', 'deletedBy']));
35+
36+
return Promise.resolve(loadedTimeline);
37+
});
38+
2539
module.exports = [
2640
validate(schema),
2741
// Validate and get projectId from the timelineId param, and set to request params for
2842
// checking by the permissions middleware
2943
validateTimeline.validateTimelineIdParam,
3044
permissions('timeline.view'),
3145
(req, res, next) => {
32-
eClient.get({ index: ES_TIMELINE_INDEX,
46+
// when user query with db, bypass the elasticsearch
47+
// and get the data directly from database
48+
if (req.query.db) {
49+
req.log.debug('bypass ES, gets timeline directly from database');
50+
return loadMilestones(req.timeline).then(timeline => res.json(timeline));
51+
}
52+
return eClient.get({ index: ES_TIMELINE_INDEX,
3353
type: ES_TIMELINE_TYPE,
3454
id: req.params.timelineId,
3555
})
3656
.then((doc) => {
3757
req.log.debug('timeline found in ES');
38-
res.json(doc._source); // eslint-disable-line no-underscore-dangle
58+
return res.json(doc._source); // eslint-disable-line no-underscore-dangle
3959
})
4060
.catch((err) => {
4161
if (err.status === 404) {
4262
req.log.debug('No timeline found in ES');
43-
// Load the milestones
44-
return req.timeline.getMilestones()
45-
.then((milestones) => {
46-
const timeline = _.omit(req.timeline.toJSON(), ['deletedAt', 'deletedBy']);
47-
timeline.milestones =
48-
_.map(milestones, milestone => _.omit(milestone.toJSON(), ['deletedAt', 'deletedBy']));
49-
50-
// Write to response
51-
return res.json(timeline);
52-
});
63+
return loadMilestones(req.timeline).then(timeline => res.json(timeline));
5364
}
5465
return next(err);
5566
});

src/routes/timelines/get.spec.js

Lines changed: 117 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,51 @@
33
*/
44
import chai from 'chai';
55
import request from 'supertest';
6+
import config from 'config';
7+
import _ from 'lodash';
68

79
import models from '../../models';
810
import server from '../../app';
911
import testUtil from '../../tests/util';
1012

1113
const should = chai.should();
1214

15+
const ES_TIMELINE_INDEX = config.get('elasticsearchConfig.timelineIndexName');
16+
const ES_TIMELINE_TYPE = config.get('elasticsearchConfig.timelineDocType');
17+
18+
const timelines = [
19+
{
20+
name: 'name 1',
21+
description: 'description 1',
22+
startDate: '2018-05-11T00:00:00.000Z',
23+
endDate: '2018-05-12T00:00:00.000Z',
24+
reference: 'project',
25+
referenceId: 1,
26+
createdBy: 1,
27+
updatedBy: 1,
28+
},
29+
{
30+
name: 'name 2',
31+
description: 'description 2',
32+
startDate: '2018-05-12T00:00:00.000Z',
33+
endDate: '2018-05-13T00:00:00.000Z',
34+
reference: 'phase',
35+
referenceId: 1,
36+
createdBy: 1,
37+
updatedBy: 1,
38+
},
39+
{
40+
name: 'name 3',
41+
description: 'description 3',
42+
startDate: '2018-05-13T00:00:00.000Z',
43+
endDate: '2018-05-14T00:00:00.000Z',
44+
reference: 'phase',
45+
referenceId: 1,
46+
createdBy: 1,
47+
updatedBy: 1,
48+
deletedAt: '2018-05-14T00:00:00.000Z',
49+
},
50+
];
1351
const milestones = [
1452
{
1553
id: 1,
@@ -143,41 +181,37 @@ describe('GET timeline', () => {
143181
]))
144182
.then(() =>
145183
// Create timelines
146-
models.Timeline.bulkCreate([
147-
{
148-
name: 'name 1',
149-
description: 'description 1',
150-
startDate: '2018-05-11T00:00:00.000Z',
151-
endDate: '2018-05-12T00:00:00.000Z',
152-
reference: 'project',
153-
referenceId: 1,
154-
createdBy: 1,
155-
updatedBy: 1,
156-
},
157-
{
158-
name: 'name 2',
159-
description: 'description 2',
160-
startDate: '2018-05-12T00:00:00.000Z',
161-
endDate: '2018-05-13T00:00:00.000Z',
162-
reference: 'phase',
163-
referenceId: 1,
164-
createdBy: 1,
165-
updatedBy: 1,
166-
},
167-
{
168-
name: 'name 3',
169-
description: 'description 3',
170-
startDate: '2018-05-13T00:00:00.000Z',
171-
endDate: '2018-05-14T00:00:00.000Z',
172-
reference: 'phase',
173-
referenceId: 1,
174-
createdBy: 1,
175-
updatedBy: 1,
176-
deletedAt: '2018-05-14T00:00:00.000Z',
177-
},
178-
]))
179-
.then(() => models.Milestone.bulkCreate(milestones))
180-
.then(() => done());
184+
// Create timelines
185+
models.Timeline.bulkCreate(timelines, { returning: true })
186+
.then(createdTimelines => (
187+
// create milestones after timelines
188+
models.Milestone.bulkCreate(milestones))
189+
.then(createdMilestones => [createdTimelines, createdMilestones]),
190+
),
191+
).then(([createdTimelines, createdMilestones]) =>
192+
// Index to ES
193+
Promise.all(_.map(createdTimelines, async (createdTimeline) => {
194+
const timelineJson = _.omit(createdTimeline.toJSON(), 'deletedAt', 'deletedBy');
195+
timelineJson.projectId = createdTimeline.id !== 3 ? 1 : 2;
196+
if (timelineJson.id === 1) {
197+
timelineJson.milestones = _.map(
198+
createdMilestones,
199+
cm => _.omit(cm.toJSON(), 'deletedAt', 'deletedBy'),
200+
);
201+
} else if (timelineJson.id === 2) {
202+
timelineJson.description = 'from ES';
203+
}
204+
205+
await server.services.es.index({
206+
index: ES_TIMELINE_INDEX,
207+
type: ES_TIMELINE_TYPE,
208+
id: timelineJson.id,
209+
body: timelineJson,
210+
});
211+
}))
212+
.then(() => {
213+
done();
214+
}));
181215
});
182216
});
183217
});
@@ -316,5 +350,53 @@ describe('GET timeline', () => {
316350
})
317351
.expect(200, done);
318352
});
353+
354+
it('should return data from ES when db param is not set', (done) => {
355+
request(server)
356+
.get('/v5/timelines/2')
357+
.set({
358+
Authorization: `Bearer ${testUtil.jwts.admin}`,
359+
})
360+
.expect(200)
361+
.end((err, res) => {
362+
const resJson = res.body;
363+
resJson.id.should.be.eql(2);
364+
resJson.name.should.be.eql('name 2');
365+
resJson.description.should.be.eql('from ES');
366+
367+
resJson.startDate.should.be.eql('2018-05-12T00:00:00.000Z');
368+
resJson.endDate.should.be.eql('2018-05-13T00:00:00.000Z');
369+
resJson.reference.should.be.eql('phase');
370+
resJson.referenceId.should.be.eql(1);
371+
372+
resJson.createdBy.should.be.eql(1);
373+
should.exist(resJson.createdAt);
374+
resJson.updatedBy.should.be.eql(1);
375+
should.exist(resJson.updatedAt);
376+
should.not.exist(resJson.deletedBy);
377+
should.not.exist(resJson.deletedAt);
378+
379+
should.not.exist(resJson.milestones);
380+
381+
done();
382+
});
383+
});
384+
385+
it('should return data from DB without calling ES when db param is set', (done) => {
386+
request(server)
387+
.get('/v5/timelines/2?db=true')
388+
.set({
389+
Authorization: `Bearer ${testUtil.jwts.admin}`,
390+
})
391+
.expect(200)
392+
.end((err, res) => {
393+
const resJson = res.body;
394+
resJson.id.should.be.eql(2);
395+
resJson.name.should.be.eql('name 2');
396+
resJson.description.should.be.eql('description 2');
397+
398+
done();
399+
});
400+
});
319401
});
320402
});

0 commit comments

Comments
 (0)