Skip to content

Commit 6773ea3

Browse files
authored
[O2B-1492] Add status history filter to environments overview (#2034)
* Adds a status history filter to the environments overview page UI. * Includes special handling for status histories ending with 'X'. * Adds tests to verify correct filtering behaviour. * Additionally colourises the status history legend to make it easier to compare with the table colours.
1 parent aace7a6 commit 6773ea3

File tree

6 files changed

+85
-14
lines changed

6 files changed

+85
-14
lines changed

lib/public/components/environments/environmentStatusHistoryComponent.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export const environmentStatusHistoryLegendComponent = () =>
2525
h('h5', 'Status History Legend'),
2626
Object.keys(StatusAcronym).map((status) =>
2727
h('.flex-row.justify-between', [
28-
h('', status),
29-
h('', StatusAcronym[status]),
28+
coloredEnvironmentStatusComponent(status),
29+
h('', coloredEnvironmentStatusComponent(status, StatusAcronym[status])),
3030
])),
3131
]);
3232

lib/public/views/Environments/ActiveColumns/environmentsActiveColumns.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,16 @@ export const environmentsActiveColumns = {
116116
coloredEnvironmentStatusComponent(status, StatusAcronym[status]),
117117
]).slice(1),
118118
balloon: true,
119+
120+
/**
121+
* Status history filter component
122+
*
123+
* @param {EnvironmentOverviewModel} environmentOverviewModel the environment overview model
124+
* @return {Component} the filter component
125+
*/
126+
filter: (environmentOverviewModel) => rawTextFilter(
127+
environmentOverviewModel.filteringModel.get('statusHistory'),
128+
{ classes: ['w-100'], placeholder: 'e.g. D-R-X' },
129+
),
119130
},
120131
};

lib/public/views/Environments/Overview/EnvironmentOverviewModel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class EnvironmentOverviewModel extends OverviewPageModel {
3232
super();
3333

3434
this._filteringModel = new FilteringModel({
35+
statusHistory: new RawTextFilterModel(),
3536
currentStatus: new SelectionFilterModel({
3637
availableOptions: Object.keys(StatusAcronym).map((status) => ({
3738
value: status,

lib/usecases/environment/GetAllEnvironmentsUseCase.js

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ class GetAllEnvironmentsUseCase {
126126
}
127127

128128
if (statusHistory) {
129-
// Split the string into separate characters
130-
const historyItems = statusHistory.split('');
129+
// Check if status history ends with 'X' and remove it if present to handle the special case later
130+
const containsX = statusHistory.endsWith('X');
131+
const cleanedStatusHistory = containsX ? statusHistory.slice(0, -1) : statusHistory;
132+
const historyItems = cleanedStatusHistory.split('');
131133

132134
// Swap the acronyms with the status (=acronym -> status)
133135
const acronymToStatus = {};
@@ -145,17 +147,38 @@ class GetAllEnvironmentsUseCase {
145147
}
146148
}
147149

148-
// Filter the environments by status history using the subquery
149-
filterQueryBuilder.literalWhere(
150-
`${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} = :statusFilters`,
151-
// Create a string of the status filters separated by a comma
152-
{ statusFilters: statusFilters.join(',') },
153-
);
150+
if (containsX) {
151+
const statusFiltersWithDestroyed = [...statusFilters, 'DESTROYED'].join(',');
152+
const statusFiltersWithDone = [...statusFilters, 'DONE'].join(',');
153+
154+
/*
155+
* Use OR condition to match subsequences ending with either DESTROYED or DONE
156+
* Filter the environments by using LIKE for subsequence matching
157+
*/
158+
filterQueryBuilder.literalWhere(
159+
`(${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFiltersWithDestroyed OR ` +
160+
`${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFiltersWithDone)`,
161+
{
162+
statusFiltersWithDestroyed: `%${statusFiltersWithDestroyed}`,
163+
statusFiltersWithDone: `%${statusFiltersWithDone}`,
164+
},
165+
);
154166

155-
filterQueryBuilder.includeAttribute({
156-
query: ENVIRONMENT_STATUS_HISTORY_SUBQUERY,
157-
alias: 'statusHistory',
158-
});
167+
filterQueryBuilder.includeAttribute({
168+
query: ENVIRONMENT_STATUS_HISTORY_SUBQUERY,
169+
alias: 'statusHistory',
170+
});
171+
} else {
172+
filterQueryBuilder.literalWhere(
173+
`${ENVIRONMENT_STATUS_HISTORY_SUBQUERY} LIKE :statusFilters`,
174+
{ statusFilters: `%${statusFilters.join(',')}%` },
175+
);
176+
177+
filterQueryBuilder.includeAttribute({
178+
query: ENVIRONMENT_STATUS_HISTORY_SUBQUERY,
179+
alias: 'statusHistory',
180+
});
181+
}
159182
}
160183

161184
if (runNumbersExpression) {

test/api/environments.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,15 @@ module.exports = () => {
171171
expect(withChar[1].id).to.be.equal(withoutChar[1].id);
172172
});
173173

174+
it('should successfully filter environments on status history with a partial sequence', async () => {
175+
const response = await request(server).get('/api/environments?filter[statusHistory]=D-E');
176+
177+
expect(response.status).to.equal(200);
178+
const environments = response.body.data;
179+
expect(environments.length).to.be.equal(1);
180+
expect(environments[0].id).to.be.equal('KGIS12DS');
181+
});
182+
174183
it('should successfully filter environments status history with limit', async () => {
175184
const response = await request(server).get('/api/environments?filter[statusHistory]=SE&page[limit]=1');
176185

test/public/envs/overview.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,33 @@ module.exports = () => {
294294
await page.waitForSelector(filterPanelSelector, { visible: true });
295295
});
296296

297+
it('should successfully filter environments by their status history', async () => {
298+
/**
299+
* This is the sequence to test filtering the environments on their status history.
300+
*
301+
* @param {string} selector the filter input selector
302+
* @param {string} inputValue the value to type in the filter input
303+
* @param {string[]} expectedIds the list of expected environment IDs after filtering
304+
* @return {void}
305+
*/
306+
const filterOnStatusHistory = async (selector, inputValue, expectedIds) => {
307+
await fillInput(page, selector, inputValue, ['change']);
308+
await waitForTableLength(page, expectedIds.length);
309+
expect(await page.$$eval('tbody tr', (rows) => rows.map((row) => row.id))).to.eql(expectedIds.map(id => `row${id}`));
310+
};
311+
312+
await expectAttributeValue(page, '.historyItems-filter input', 'placeholder', 'e.g. D-R-X');
313+
314+
await filterOnStatusHistory('.historyItems-filter input', 'C-R-D-X', ['TDI59So3d']);
315+
await resetFilters(page);
316+
317+
await filterOnStatusHistory('.historyItems-filter input', 'S-E', ['EIDO13i3D', '8E4aZTjY']);
318+
await resetFilters(page);
319+
320+
await filterOnStatusHistory('.historyItems-filter input', 'D-E', ['KGIS12DS']);
321+
await resetFilters(page);
322+
});
323+
297324
it('should successfully filter environments by their current status', async () => {
298325
/**
299326
* Checks that all the rows of the given table have a valid current status

0 commit comments

Comments
 (0)