Skip to content

Commit 4202798

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/globals-17.4.0
2 parents edbe6d7 + 25e7028 commit 4202798

6 files changed

Lines changed: 85 additions & 53 deletions

File tree

lib/usecases/environment/GetAllEnvironmentsUseCase.js

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const { dataSource } = require('../../database/DataSource.js');
2323
const { statusAcronyms } = require('../../domain/enums/StatusAcronyms.js');
2424
const { unpackNumberRange } = require('../../utilities/rangeUtils.js');
2525
const { splitStringToStringsTrimmed } = require('../../utilities/stringUtils.js');
26+
const { setTimeRangeQuery } = require('../../utilities/setTimeRangeQuery.js');
2627

2728
/**
2829
* Subquery to select the latest history item for each environment.
@@ -69,18 +70,11 @@ class GetAllEnvironmentsUseCase {
6970
const { filter, page = {} } = query;
7071
const { limit = ApiConfig.pagination.limit, offset = 0 } = page;
7172

72-
/**
73-
* Prepare a query builder with ordering, limit and offset
74-
*
75-
* @return {QueryBuilder} the created query builder
76-
*/
77-
const prepareQueryBuilder = () => dataSource.createQueryBuilder()
73+
const queryBuilder = dataSource.createQueryBuilder()
7874
.orderBy('updatedAt', 'desc')
7975
.limit(limit)
8076
.offset(offset);
8177

82-
const fetchQueryBuilder = prepareQueryBuilder();
83-
8478
if (filter) {
8579
const {
8680
ids: idsExpression,
@@ -90,38 +84,34 @@ class GetAllEnvironmentsUseCase {
9084
created,
9185
} = filter;
9286

93-
const filterQueryBuilder = prepareQueryBuilder();
94-
9587
if (created) {
96-
const from = created.from !== undefined ? created.from : 0;
97-
const to = created.to !== undefined ? created.to : Date.now();
98-
filterQueryBuilder.where('createdAt').between(from, to);
88+
setTimeRangeQuery(created, 'createdAt', queryBuilder);
9989
}
10090

10191
if (idsExpression) {
10292
const filters = idsExpression.split(',').map((id) => id.trim());
10393

10494
// Filter should be like with only one filter
10595
if (filters.length === 1) {
106-
filterQueryBuilder.where('id').substring(filters[0]);
96+
queryBuilder.where('id').substring(filters[0]);
10797
}
10898

10999
// Filters should be exact with more than one filter
110100
if (filters.length > 1) {
111-
filterQueryBuilder.andWhere({ id: { [Op.in]: filters } });
101+
queryBuilder.andWhere({ id: { [Op.in]: filters } });
112102
}
113103
}
114104

115105
if (currentStatusExpression) {
116106
const filters = currentStatusExpression.split(',').map((status) => status.trim());
117107

118108
// Filter the environments by current status using the subquery
119-
filterQueryBuilder.literalWhere(
109+
queryBuilder.literalWhere(
120110
`${ENVIRONMENT_LATEST_HISTORY_ITEM_SUBQUERY} IN (:filters)`,
121111
{ filters },
122112
);
123113

124-
filterQueryBuilder.includeAttribute({
114+
queryBuilder.includeAttribute({
125115
query: ENVIRONMENT_LATEST_HISTORY_ITEM_SUBQUERY,
126116
alias: 'currentStatus',
127117
});
@@ -157,7 +147,7 @@ class GetAllEnvironmentsUseCase {
157147
* Use OR condition to match subsequences ending with either DESTROYED or DONE
158148
* Filter the environments by using LIKE for subsequence matching
159149
*/
160-
filterQueryBuilder.literalWhere(
150+
queryBuilder.literalWhere(
161151
`(${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFiltersWithDestroyed OR ` +
162152
`${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFiltersWithDone)`,
163153
{
@@ -166,17 +156,17 @@ class GetAllEnvironmentsUseCase {
166156
},
167157
);
168158

169-
filterQueryBuilder.includeAttribute({
159+
queryBuilder.includeAttribute({
170160
query: ENVIRONMENT_STATUS_HISTORY_SUBQUERY,
171161
alias: 'statusHistory',
172162
});
173163
} else {
174-
filterQueryBuilder.literalWhere(
164+
queryBuilder.literalWhere(
175165
`${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFilters`,
176166
{ statusFilters: `%${statusFilters.join(',')}%` },
177167
);
178168

179-
filterQueryBuilder.includeAttribute({
169+
queryBuilder.includeAttribute({
180170
query: ENVIRONMENT_STATUS_HISTORY_SUBQUERY,
181171
alias: 'statusHistory',
182172
});
@@ -190,30 +180,20 @@ class GetAllEnvironmentsUseCase {
190180

191181
// Check that the final run numbers list contains at least one valid run number
192182
if (finalRunNumberList.length > 0) {
193-
filterQueryBuilder.include({
183+
queryBuilder.include({
194184
association: 'runs',
195185
where: {
196186
// Filter should be like with only one filter and exact with more than one filter
197187
runNumber: { [finalRunNumberList.length === 1 ? Op.substring : Op.in]: finalRunNumberList },
198188
},
199189
});
200190
}
201-
};
202-
203-
const filteredEnvironmentsIds = (await EnvironmentRepository.findAll(filterQueryBuilder)).map(({ id }) => id);
204-
// If no environments match the filter, return an empty result
205-
if (filteredEnvironmentsIds.length === 0) {
206-
return {
207-
count: 0,
208-
environments: [],
209-
};
210191
}
211-
fetchQueryBuilder.where('id').oneOf(filteredEnvironmentsIds);
212192
}
213193

214-
fetchQueryBuilder.include({ association: 'runs' });
215-
fetchQueryBuilder.include({ association: 'historyItems' });
216-
const { count, rows } = await EnvironmentRepository.findAndCountAll(fetchQueryBuilder);
194+
queryBuilder.include({ association: 'runs' });
195+
queryBuilder.include({ association: 'historyItems' });
196+
const { count, rows } = await EnvironmentRepository.findAndCountAll(queryBuilder);
217197
return {
218198
count,
219199
environments: rows.map((environment) => environmentAdapter.toEntity(environment)),

lib/usecases/lhcFill/GetAllLhcFillsUseCase.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const { ApiConfig } = require('../../config/index.js');
2424
const { RunDefinition } = require('../../domain/enums/RunDefinition.js');
2525
const { unpackNumberRange } = require('../../utilities/rangeUtils.js');
2626
const { splitStringToStringsTrimmed } = require('../../utilities/stringUtils.js');
27+
const { setTimeRangeQuery } = require('../../utilities/setTimeRangeQuery.js');
2728

2829
/**
2930
* GetAllLhcFillsUseCase
@@ -54,15 +55,11 @@ class GetAllLhcFillsUseCase {
5455
}
5556

5657
if (stableBeamsStart) {
57-
const from = stableBeamsStart.from !== undefined ? stableBeamsStart.from : 0;
58-
const to = stableBeamsStart.to !== undefined ? stableBeamsStart.to : new Date().getTime();
59-
queryBuilder.where('stableBeamsStart').between(from, to);
58+
setTimeRangeQuery(stableBeamsStart, 'stableBeamsStart', queryBuilder);
6059
}
6160

6261
if (stableBeamsEnd) {
63-
const from = stableBeamsEnd.from !== undefined ? stableBeamsEnd.from : 0;
64-
const to = stableBeamsEnd.to !== undefined ? stableBeamsEnd.to : new Date().getTime();
65-
queryBuilder.where('stableBeamsEnd').between(from, to);
62+
setTimeRangeQuery(stableBeamsEnd, 'stableBeamsEnd', queryBuilder);
6663
}
6764

6865
if (fillNumbers) {

lib/usecases/log/GetAllLogsUseCase.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const { ApiConfig } = require('../../config/index.js');
2929
const { Op } = require('sequelize');
3030
const { dataSource } = require('../../database/DataSource.js');
3131
const { checkForFilterExclusion } = require('../common/checkForFilterExclusion.js');
32+
const { setTimeRangeQuery } = require('../../utilities/setTimeRangeQuery.js');
3233

3334
/**
3435
* Apply the given filter on the given query builder
@@ -73,9 +74,7 @@ const applyFilter = async (dataSource, queryBuilder, filter) => {
7374
}
7475

7576
if (created) {
76-
const from = created.from !== undefined ? created.from : 0;
77-
const to = created.to !== undefined ? created.to : new Date().getTime();
78-
queryBuilder.where('createdAt').between(from, to);
77+
setTimeRangeQuery(created, 'createdAt', queryBuilder);
7978
}
8079

8180
if (origin) {

lib/usecases/run/GetAllRunsUseCase.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const { qcFlagSummaryService } = require('../../server/services/qualityControlFl
2525
const { DetectorType } = require('../../domain/enums/DetectorTypes.js');
2626
const { unpackNumberRange } = require('../../utilities/rangeUtils.js');
2727
const { splitStringToStringsTrimmed } = require('../../utilities/stringUtils.js');
28+
const { setTimeRangeQuery } = require('../../utilities/setTimeRangeQuery.js');
2829

2930
/**
3031
* GetAllRunsUseCase
@@ -151,21 +152,15 @@ class GetAllRunsUseCase {
151152
}
152153

153154
if (o2start) {
154-
const from = o2start.from !== undefined ? o2start.from : 0;
155-
const to = o2start.to !== undefined ? o2start.to : new Date().getTime();
156-
filteringQueryBuilder.where('timeO2Start').between(from, to);
155+
setTimeRangeQuery(o2start, 'timeO2Start', filteringQueryBuilder);
157156
}
158157

159158
if (o2end) {
160-
const from = o2end.from !== undefined ? o2end.from : 0;
161-
const to = o2end.to !== undefined ? o2end.to : new Date().getTime();
162-
filteringQueryBuilder.where('timeO2End').between(from, to);
159+
setTimeRangeQuery(o2end, 'timeO2End', filteringQueryBuilder);
163160
}
164161

165162
if (updatedAt) {
166-
const from = updatedAt.from ?? 0;
167-
const to = updatedAt.to ?? new Date().getTime();
168-
filteringQueryBuilder.where('updatedAt').between(from, to);
163+
setTimeRangeQuery(updatedAt, 'updatedAt', filteringQueryBuilder);
169164
}
170165

171166
if (triggerValues) {

lib/utilities/setTimeRangeQuery.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
14+
/**
15+
* Function that sets a time range in a QueryBuilder.
16+
*
17+
* @param {object} timerange an object that defines a time range to add to the query
18+
* @param {number} timerange.from the lower bound of the time range
19+
* @param {number} timerange.to the upper bound of the time range
20+
* @param {string} attribute the model attribute for which the range will be set
21+
* @param {QueryBuilder} queryBuilder queryBuider instance in which the time range will be set.
22+
* @returns {void}
23+
*/
24+
exports.setTimeRangeQuery = ({ from = 0, to = Date.now() }, attribute, queryBuilder) =>
25+
queryBuilder.where(attribute).between(from, to);

test/lib/usecases/environment/GetAllEnvironmentsUseCase.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,40 @@ module.exports = () => {
225225
expect(environments).to.be.an('array');
226226
expect(environments.length).to.be.equal(0); // Environments from seeders
227227
});
228+
229+
it('should return correct total count and all filtered results across pages', async () => {
230+
const totalMatchingFilter = 6; // 'RUNNING, ERROR' matches 6 environments at this point
231+
const limit = 2;
232+
233+
// First page
234+
getAllEnvsDto.query = { page: { limit, offset: 0 }, filter: { currentStatus: 'RUNNING, ERROR' } };
235+
const page1 = await new GetAllEnvironmentsUseCase().execute(getAllEnvsDto);
236+
237+
expect(page1.count).to.be.equal(totalMatchingFilter);
238+
expect(page1.environments).to.be.an('array');
239+
expect(page1.environments.length).to.be.equal(limit);
240+
241+
// Second page
242+
getAllEnvsDto.query = { page: { limit, offset: 2 }, filter: { currentStatus: 'RUNNING, ERROR' } };
243+
const page2 = await new GetAllEnvironmentsUseCase().execute(getAllEnvsDto);
244+
245+
expect(page2.count).to.be.equal(totalMatchingFilter);
246+
expect(page2.environments).to.be.an('array');
247+
expect(page2.environments.length).to.be.equal(limit);
248+
249+
// Third page
250+
getAllEnvsDto.query = { page: { limit, offset: 4 }, filter: { currentStatus: 'RUNNING, ERROR' } };
251+
const page3 = await new GetAllEnvironmentsUseCase().execute(getAllEnvsDto);
252+
253+
expect(page3.count).to.be.equal(totalMatchingFilter);
254+
expect(page3.environments).to.be.an('array');
255+
expect(page3.environments.length).to.be.equal(limit);
256+
257+
// Collect all environment IDs and verify no duplicates and all present
258+
const allIds = [page1, page2, page3].flatMap(({ environments })=> environments.map(({ id }) => id));
259+
260+
expect(allIds.length).to.be.equal(totalMatchingFilter);
261+
expect(new Set(allIds).size).to.be.equal(totalMatchingFilter);
262+
expect(allIds).to.have.members(['SomeId', 'newId', 'CmCvjNbg', 'EIDO13i3D', '8E4aZTjY', 'Dxi029djX']);
263+
});
228264
};

0 commit comments

Comments
 (0)