From 71a7c3004e244f3a79a41d577dbf0266df0ae272 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:10:53 +0100 Subject: [PATCH 1/4] [O2B-1491] Add current status filtering for environments Introduces a filter for the environment's current status. Make backend filtering logic handle unknown statuses and to handle known 'MIXED' and 'DONE' statuses. Updates tests to verify filtering functionality. --- lib/domain/enums/StatusAcronyms.js | 2 ++ .../environmentsActiveColumns.js | 9 ++++++ .../Overview/EnvironmentOverviewModel.js | 10 +++++++ .../environment/GetAllEnvironmentsUseCase.js | 16 ++++++---- test/public/envs/overview.test.js | 29 +++++++++++++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/lib/domain/enums/StatusAcronyms.js b/lib/domain/enums/StatusAcronyms.js index f29ae1b56c..c24f0db644 100644 --- a/lib/domain/enums/StatusAcronyms.js +++ b/lib/domain/enums/StatusAcronyms.js @@ -18,7 +18,9 @@ const statusAcronyms = Object.freeze({ CONFIGURED: 'C', RUNNING: 'R', ERROR: 'E', + MIXED: 'M', DESTROYED: 'X', + DONE: 'X', }); exports.statusAcronyms = statusAcronyms; diff --git a/lib/public/views/Environments/ActiveColumns/environmentsActiveColumns.js b/lib/public/views/Environments/ActiveColumns/environmentsActiveColumns.js index c570931869..31681ae8cb 100644 --- a/lib/public/views/Environments/ActiveColumns/environmentsActiveColumns.js +++ b/lib/public/views/Environments/ActiveColumns/environmentsActiveColumns.js @@ -24,6 +24,7 @@ import { environmentStatusHistoryLegendComponent } from '../../../components/env import { infoTooltip } from '../../../components/common/popover/infoTooltip.js'; import { aliEcsEnvironmentLinkComponent } from '../../../components/common/externalLinks/aliEcsEnvironmentLinkComponent.js'; import { StatusAcronym } from '../../../domain/enums/statusAcronym.mjs'; +import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js'; /** * List of active columns for a generic Environments component @@ -83,6 +84,14 @@ export const environmentsActiveColumns = { size: 'w-10', noEllipsis: true, format: (_, environment) => displayEnvironmentStatus(environment), + + /** + * Status filter component + * + * @param {EnvironmentOverviewModel} environmentOverviewModel the environment overview model + * @return {Component} the filter component + */ + filter: (environmentOverviewModel) => checkboxes(environmentOverviewModel.filteringModel.get('currentStatus').selectionModel), }, historyItems: { name: h('.flex-row.g2.items-center', ['Status History', infoTooltip(environmentStatusHistoryLegendComponent())]), diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index 1d701b4a27..ba7d717b58 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -14,7 +14,10 @@ import { buildUrl } from '/js/src/index.js'; import { FilteringModel } from '../../../components/Filters/common/FilteringModel.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; +import { SelectionFilterModel } from '../../../components/Filters/common/filters/SelectionFilterModel.js'; import { debounce } from '../../../utilities/debounce.js'; +import { coloredEnvironmentStatusComponent } from '../ColoredEnvironmentStatusComponent.js'; +import { StatusAcronym } from '../../../domain/enums/statusAcronym.mjs'; /** * Environment overview page model @@ -28,6 +31,13 @@ export class EnvironmentOverviewModel extends OverviewPageModel { super(); this._filteringModel = new FilteringModel({ + currentStatus: new SelectionFilterModel({ + availableOptions: Object.keys(StatusAcronym).map((status) => ({ + value: status, + label: coloredEnvironmentStatusComponent(status), + rawLabel: status, + })).concat({ value: 'UNKNOWN', label: coloredEnvironmentStatusComponent('UNKNOWN'), rawLabel: 'UNKNOWN' }), + }), }); this._filteringModel.observe(() => this._applyFilters(true)); diff --git a/lib/usecases/environment/GetAllEnvironmentsUseCase.js b/lib/usecases/environment/GetAllEnvironmentsUseCase.js index 5751d8ee88..770743c125 100644 --- a/lib/usecases/environment/GetAllEnvironmentsUseCase.js +++ b/lib/usecases/environment/GetAllEnvironmentsUseCase.js @@ -27,17 +27,21 @@ const { statusAcronyms } = require('../../domain/enums/StatusAcronyms.js'); * It orders the history items by updatedAt DESC and selects to have the latests * history item on top. Then it limits the result to 1, so only * the latest history item is selected. + * the latest history item is selected. If the environment has no history + * items, it returns 'UNKNOWN' to indicate the environment has never had a status. * * Environment refers to the current environment in the main query, * because this query is a sub query that is executed for every environment. */ const ENVIRONMENT_LATEST_HISTORY_ITEM_SUBQUERY = ` -(SELECT h.status - FROM environments_history_items AS h - WHERE h.environment_id = Environment.id - ORDER BY h.updated_at DESC - LIMIT 1 -)`; +(CASE + WHEN EXISTS(SELECT 1 FROM environments_history_items AS h WHERE h.environment_id = Environment.id) THEN + (SELECT h.status + FROM environments_history_items AS h WHERE h.environment_id = Environment.id + ORDER BY h.updated_at DESC + LIMIT 1) + ELSE 'UNKNOWN' +END)`; /** * Subquery to select the status history for each environment. diff --git a/test/public/envs/overview.test.js b/test/public/envs/overview.test.js index ae673ef4e0..058830a02e 100644 --- a/test/public/envs/overview.test.js +++ b/test/public/envs/overview.test.js @@ -290,4 +290,33 @@ module.exports = () => { await openFilteringPanel(page); await page.waitForSelector(filterPanelSelector, { visible: true }); }); + + it('should successfully filter environments by their current status', async () => { + /** + * Checks that all the rows of the given table have a valid current status + * + * @param {string[]} authorizedCurrentStatuses the list of valid current statuses + * @return {void} + */ + const checkTableCurrentStatuses = async (authorizedCurrentStatuses) => { + const rows = await page.$$('tbody tr'); + for (const row of rows) { + expect(await row.evaluate((rowItem) => { + const rowId = rowItem.id; + return document.querySelector(`#${rowId}-status-text`).innerText; + })).to.be.oneOf(authorizedCurrentStatuses); + } + }; + + const currentStatusSelectorPrefix = '.status-filter #checkboxes-checkbox-'; + const getCurrentStatusCheckboxSelector = (statusName) => `${currentStatusSelectorPrefix}${statusName}`; + + await page.$eval(getCurrentStatusCheckboxSelector("RUNNING"), (element) => element.click()); + await waitForTableLength(page, 2); + await checkTableCurrentStatuses(["RUNNING"]); + + await page.$eval(getCurrentStatusCheckboxSelector("DEPLOYED"), (element) => element.click()); + await waitForTableLength(page, 3); + await checkTableCurrentStatuses(["RUNNING", "DEPLOYED"]); + }); }; From b0a027ee1c8931a20e1be4ca9b387b4284c02d42 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:21:34 +0100 Subject: [PATCH 2/4] [O2B-1491] Missed deleting old docstring --- lib/usecases/environment/GetAllEnvironmentsUseCase.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/usecases/environment/GetAllEnvironmentsUseCase.js b/lib/usecases/environment/GetAllEnvironmentsUseCase.js index 770743c125..2db5c9bd8a 100644 --- a/lib/usecases/environment/GetAllEnvironmentsUseCase.js +++ b/lib/usecases/environment/GetAllEnvironmentsUseCase.js @@ -26,7 +26,6 @@ const { statusAcronyms } = require('../../domain/enums/StatusAcronyms.js'); * Subquery to select the latest history item for each environment. * It orders the history items by updatedAt DESC and selects to have the latests * history item on top. Then it limits the result to 1, so only - * the latest history item is selected. * the latest history item is selected. If the environment has no history * items, it returns 'UNKNOWN' to indicate the environment has never had a status. * From 661e4dc1a7405e9f8536182a830d9d2c6b3e7d35 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:12:08 +0100 Subject: [PATCH 3/4] [O2B-1491] Fix syntax error in overview.test.js --- test/public/envs/overview.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/public/envs/overview.test.js b/test/public/envs/overview.test.js index 2acebdf0da..b6e9183798 100644 --- a/test/public/envs/overview.test.js +++ b/test/public/envs/overview.test.js @@ -321,7 +321,7 @@ module.exports = () => { await page.$eval(getCurrentStatusCheckboxSelector("DEPLOYED"), (element) => element.click()); await waitForTableLength(page, 3); await checkTableCurrentStatuses(["RUNNING", "DEPLOYED"]); - }; + }); it('should successfully filter environments by their IDs', async () => { /** From c6314ab32c6f0da9988003092b553693f6ea919f Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:12:45 +0100 Subject: [PATCH 4/4] [O2B-1491] Remove 'MIXED' status and filtering by 'UNKNOWN' As a result of PR comments removed the status mixed from possible statuses ENUM. Also removed filtering by the 'UNKNOWN' status given it is an unreachable status. --- lib/domain/enums/StatusAcronyms.js | 1 - lib/public/domain/enums/statusAcronym.mjs | 1 - .../Overview/EnvironmentOverviewModel.js | 2 +- .../environment/GetAllEnvironmentsUseCase.js | 17 +++++++---------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/domain/enums/StatusAcronyms.js b/lib/domain/enums/StatusAcronyms.js index c24f0db644..d70f7c8752 100644 --- a/lib/domain/enums/StatusAcronyms.js +++ b/lib/domain/enums/StatusAcronyms.js @@ -18,7 +18,6 @@ const statusAcronyms = Object.freeze({ CONFIGURED: 'C', RUNNING: 'R', ERROR: 'E', - MIXED: 'M', DESTROYED: 'X', DONE: 'X', }); diff --git a/lib/public/domain/enums/statusAcronym.mjs b/lib/public/domain/enums/statusAcronym.mjs index 3564005608..6ce0acaca6 100644 --- a/lib/public/domain/enums/statusAcronym.mjs +++ b/lib/public/domain/enums/statusAcronym.mjs @@ -18,7 +18,6 @@ export const StatusAcronym = Object.freeze({ CONFIGURED: 'C', RUNNING: 'R', ERROR: 'E', - MIXED: 'M', DESTROYED: 'X', DONE: 'X', }); diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index 804f012b2e..94a5563a63 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -37,7 +37,7 @@ export class EnvironmentOverviewModel extends OverviewPageModel { value: status, label: coloredEnvironmentStatusComponent(status), rawLabel: status, - })).concat({ value: 'UNKNOWN', label: coloredEnvironmentStatusComponent('UNKNOWN'), rawLabel: 'UNKNOWN' }), + })), }), ids: new RawTextFilterModel(), }); diff --git a/lib/usecases/environment/GetAllEnvironmentsUseCase.js b/lib/usecases/environment/GetAllEnvironmentsUseCase.js index 2db5c9bd8a..5751d8ee88 100644 --- a/lib/usecases/environment/GetAllEnvironmentsUseCase.js +++ b/lib/usecases/environment/GetAllEnvironmentsUseCase.js @@ -26,21 +26,18 @@ const { statusAcronyms } = require('../../domain/enums/StatusAcronyms.js'); * Subquery to select the latest history item for each environment. * It orders the history items by updatedAt DESC and selects to have the latests * history item on top. Then it limits the result to 1, so only - * the latest history item is selected. If the environment has no history - * items, it returns 'UNKNOWN' to indicate the environment has never had a status. + * the latest history item is selected. * * Environment refers to the current environment in the main query, * because this query is a sub query that is executed for every environment. */ const ENVIRONMENT_LATEST_HISTORY_ITEM_SUBQUERY = ` -(CASE - WHEN EXISTS(SELECT 1 FROM environments_history_items AS h WHERE h.environment_id = Environment.id) THEN - (SELECT h.status - FROM environments_history_items AS h WHERE h.environment_id = Environment.id - ORDER BY h.updated_at DESC - LIMIT 1) - ELSE 'UNKNOWN' -END)`; +(SELECT h.status + FROM environments_history_items AS h + WHERE h.environment_id = Environment.id + ORDER BY h.updated_at DESC + LIMIT 1 +)`; /** * Subquery to select the status history for each environment.