Skip to content

Commit 7db349f

Browse files
authored
[O2B-905] Add endpoint to fetch all replies to a given log using its id (#1423)
* O2B-905: Added endpoint to fetch all replies to a given log using its id * O2B-905: Service tests added * O2B-905: Code review tasks implemented, not final. * O2B-905: Log API tests updated * [O2B-905] last code review comments implemented * [O2B-905] Code review changes, dto, naming
1 parent e13000e commit 7db349f

5 files changed

Lines changed: 128 additions & 11 deletions

File tree

lib/server/controllers/logs.controller.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const { updateExpressResponseFromNativeError } = require('../express/updateExpre
3737
const { countedItemsToHttpView } = require('../utilities/countedItemsToHttpView');
3838
const { PaginationDto } = require('../../domain/dtos');
3939
const { DtoFactory } = require('../../domain/dtos/DtoFactory');
40+
const EntityIdDto = require('../../domain/dtos/EntityIdDto');
4041

4142
/**
4243
* Create a new log
@@ -286,14 +287,9 @@ const getLogById = async (request, response, next) => {
286287
}
287288
};
288289

290+
// eslint-disable-next-line valid-jsdoc
289291
/**
290292
* Get all root logs
291-
*
292-
* @param {Object} request The *request* object represents the HTTP request and has properties for the request query
293-
* string, parameters, body, HTTP headers, and so on.
294-
* @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an
295-
* HTTP request.
296-
* @returns {undefined}
297293
*/
298294
const getRootLogsHandler = async (request, response) => {
299295
const requestDto = await dtoValidator(
@@ -320,6 +316,33 @@ const getRootLogsHandler = async (request, response) => {
320316
}
321317
};
322318

319+
// eslint-disable-next-line valid-jsdoc
320+
/**
321+
* Get all child logs by parent id
322+
*/
323+
const getChildLogsByParentIdHandler = async (request, response) => {
324+
const requestDto = await dtoValidator(
325+
DtoFactory.paramsOnly({
326+
logId: EntityIdDto,
327+
}),
328+
request,
329+
response,
330+
);
331+
332+
if (requestDto) {
333+
try {
334+
const { params: { logId: parentLogId } } = requestDto;
335+
const childLogs = await logService.getChildLogsByParentId(parentLogId);
336+
337+
await response.status(200).json({
338+
data: childLogs,
339+
});
340+
} catch (error) {
341+
updateExpressResponseFromNativeError(response, error);
342+
}
343+
}
344+
};
345+
323346
module.exports = {
324347
createLog,
325348
getAllAttachments,
@@ -330,4 +353,5 @@ module.exports = {
330353
getLogTree,
331354
patchRun,
332355
getRootLogsHandler,
356+
getChildLogsByParentIdHandler,
333357
};

lib/server/routers/logs.router.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ module.exports = {
6161
path: 'tree',
6262
controller: LogsController.getLogTree,
6363
},
64+
{
65+
method: 'get',
66+
path: 'children',
67+
controller: LogsController.getChildLogsByParentIdHandler,
68+
},
6469
],
6570
},
6671
],

lib/server/services/log/LogService.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ class LogService {
8585
return { count, logIds };
8686
}
8787

88+
/**
89+
* Retrieves a list of replies to a log based off the given logId
90+
* @param {number} logId The id of the log of which the child logs are retrieved
91+
* @return {Promise<Log[]>} A promise that resolves with the replies logs
92+
*/
93+
async getChildLogsByParentId(logId) {
94+
const childLogQueryBuilder = this._createFindQueryBuilder()
95+
.where('parentLogId').is(logId);
96+
97+
const childLogs = await LogRepository.findAll(childLogQueryBuilder);
98+
return this._prepareLogsEntities(childLogs);
99+
}
100+
88101
/**
89102
* Returns all the logs related to a given LHC fill
90103
*
@@ -131,17 +144,36 @@ class LogService {
131144
* @return {Promise<Log[]>} the resulting logs
132145
*/
133146
async _getAllByIds(logIds) {
134-
const logQueryBuilder = dataSource.createQueryBuilder()
135-
.where('id').oneOf(...logIds)
136-
.include({ association: 'runs', attributes: ['id', 'runNumber'] })
137-
.include({ association: 'lhcFills', attributes: ['fillNumber'] })
138-
.include({ association: 'environments', attributes: ['id'] });
147+
const logQueryBuilder = this._createFindQueryBuilder();
139148

149+
logQueryBuilder.where('id').oneOf(...logIds);
140150
const logs = await LogRepository.findAll(logQueryBuilder);
151+
return this._prepareLogsEntities(logs);
152+
}
153+
154+
/**
155+
* Return all the logs after preparing them to entities and adding childrenCount
156+
*
157+
* @param {SequelizeLog[]} logs the list of logs to prepare
158+
* @return {Promise<Log[]>} the resulting prepared logs
159+
*/
160+
_prepareLogsEntities(logs) {
141161
const logEntities = logs.map(logAdapter.toEntity);
142162
return LogRepository.addChildrenCountByLog(logEntities);
143163
}
144164

165+
/**
166+
* Return a standard log QueryBuilder
167+
*
168+
* @return {QueryBuilder} the resulting QueryBuilder
169+
*/
170+
_createFindQueryBuilder() {
171+
return dataSource.createQueryBuilder()
172+
.include({ association: 'runs', attributes: ['id', 'runNumber'] })
173+
.include({ association: 'lhcFills', attributes: ['fillNumber'] })
174+
.include({ association: 'environments', attributes: ['id'] });
175+
}
176+
145177
/**
146178
* Create a log in the database and return the created instance
147179
*

test/api/logs.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ module.exports = () => {
4646
}
4747
});
4848

49+
const logWithChildren = {
50+
id: 117,
51+
expectedAmountOfChildren: 2,
52+
};
53+
const logWithoutChildren = {
54+
id: 122,
55+
expectedAmountOfChildren: 0,
56+
};
57+
4958
let logWithAttachmentsId = 0;
5059
let attachmentId = 0;
5160

@@ -1575,4 +1584,27 @@ module.exports = () => {
15751584
});
15761585
});
15771586
});
1587+
1588+
describe('GET /api/logs/:logId/children', () => {
1589+
it('should return the correct child logs of a parent log', async () => {
1590+
const response = await request(server).get(`/api/logs/${logWithChildren.id}/children`);
1591+
expect(response.status).to.equal(200);
1592+
expect(response.body.data.length).to.equal(logWithChildren.expectedAmountOfChildren);
1593+
expect(response.body.data.every((log) => log.parentLogId === logWithChildren.id));
1594+
});
1595+
1596+
it('should return 400 if the log id is not a number', async () => {
1597+
const response = await request(server).get('/api/logs/abc/children');
1598+
const { errors } = response.body;
1599+
const titleError = errors.find((err) => err.source.pointer === '/data/attributes/params/logId');
1600+
expect(response.status).to.equal(400);
1601+
expect(titleError.detail).to.equal('"params.logId" must be a number');
1602+
});
1603+
1604+
it('should return an empty array if the log has no children', async () => {
1605+
const response = await request(server).get(`/api/logs/${logWithoutChildren.id}/children`);
1606+
expect(response.status).to.equal(200);
1607+
expect(response.body.data).to.be.empty;
1608+
});
1609+
});
15781610
};

test/lib/server/services/log/LogService.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,28 @@ module.exports = () => {
6666
expect(rootLogsFirstPage.logs[0].id).to.equal(1);
6767
expect(rootLogsSecondPage.logs[0].id).to.equal(5);
6868
});
69+
70+
it('Should successfully return child logs of their parent log', async () => {
71+
const logWithChildrenId = 117;
72+
const expectedAmountOfChildren = 2;
73+
const childLogs = await logService.getChildLogsByParentId(logWithChildrenId);
74+
75+
expect(childLogs.length).to.equal(expectedAmountOfChildren);
76+
childLogs.forEach((log) => expect(log.parentLogId === logWithChildrenId));
77+
});
78+
79+
it('Should successfully return an empty array if parent has no children', async () => {
80+
const logWithoutChildrenId = 122;
81+
const childLogs = await logService.getChildLogsByParentId(logWithoutChildrenId);
82+
83+
expect(childLogs).to.be.empty;
84+
});
85+
86+
it('Should successfully return correct count when children are not found', async () => {
87+
const logWithoutChildrenId = 122;
88+
const expectedAmountOfChildren = 0;
89+
const childLogs = await logService.getChildLogsByParentId(logWithoutChildrenId);
90+
91+
expect(childLogs.length).to.equal(expectedAmountOfChildren);
92+
});
6993
};

0 commit comments

Comments
 (0)