18881: Improves process filtering by status. Adds tests.
authorLucas Di Pentima <lucas.dipentima@curii.com>
Wed, 6 Apr 2022 21:08:27 +0000 (18:08 -0300)
committerLucas Di Pentima <lucas.dipentima@curii.com>
Thu, 7 Apr 2022 17:47:23 +0000 (14:47 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima@curii.com>

src/store/processes/process.ts
src/store/project-panel/project-panel-middleware-service.ts
src/store/resource-type-filters/resource-type-filters.test.ts
src/store/resource-type-filters/resource-type-filters.ts

index 37cdd2b34668a0dc45bbb34941662a6bd6811edb..b72a0c2b10b9a280ad4a59c6167cd45d152ff520 100644 (file)
@@ -92,36 +92,40 @@ export const getProcessStatusColor = (status: string, { customs }: ArvadosTheme)
 
 export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => {
     switch (true) {
+        case containerRequest.state === ContainerRequestState.FINAL &&
+            container?.state !== ContainerState.COMPLETE:
+            // Request was finalized before its container started (or the
+            // container was cancelled)
+            return ProcessStatus.CANCELLED;
+
         case containerRequest.state === ContainerRequestState.UNCOMMITTED:
             return ProcessStatus.DRAFT;
 
-        case containerRequest.priority === 0:
-            return ProcessStatus.ONHOLD;
-
-        case container && container.state === ContainerState.COMPLETE && container.exitCode === 0:
-            return ProcessStatus.COMPLETED;
+        case container?.state === ContainerState.COMPLETE:
+            if (container?.exitCode === 0) {
+                return ProcessStatus.COMPLETED;
+            }
+            return ProcessStatus.FAILED;
 
-        case container && container.state === ContainerState.CANCELLED:
+        case container?.state === ContainerState.CANCELLED:
             return ProcessStatus.CANCELLED;
 
-        case container && (container.state === ContainerState.QUEUED ||
-            container.state === ContainerState.LOCKED):
+        case container?.state === ContainerState.QUEUED ||
+            container?.state === ContainerState.LOCKED:
+            if (containerRequest.priority === 0) {
+                return ProcessStatus.ONHOLD;
+            }
             return ProcessStatus.QUEUED;
 
-        case container && container.state === ContainerState.RUNNING &&
-            !!container.runtimeStatus.error:
-            return ProcessStatus.FAILING;
-
-        case container && container.state === ContainerState.RUNNING &&
-            !!container.runtimeStatus.warning:
-            return ProcessStatus.WARNING;
-
-        case container && container.state === ContainerState.RUNNING:
+        case container?.state === ContainerState.RUNNING:
+            if (!!container?.runtimeStatus.error) {
+                return ProcessStatus.FAILING;
+            }
+            if (!!container?.runtimeStatus.warning) {
+                return ProcessStatus.WARNING;
+            }
             return ProcessStatus.RUNNING;
 
-        case container && container.state === ContainerState.COMPLETE && container.exitCode !== 0:
-            return ProcessStatus.FAILED;
-
         default:
             return ProcessStatus.UNKNOWN;
     }
index be569b49ff0276aa031f1467aec896d08c32989b..ccfa4fff9fcc4d0a34cef6a2af20f7388344a425 100644 (file)
@@ -17,7 +17,11 @@ import { OrderBuilder, OrderDirection } from "services/api/order-builder";
 import { FilterBuilder, joinFilters } from "services/api/filter-builder";
 import { GroupContentsResource, GroupContentsResourcePrefix } from "services/groups-service/groups-service";
 import { updateFavorites } from "store/favorites/favorites-actions";
-import { IS_PROJECT_PANEL_TRASHED, projectPanelActions, getProjectPanelCurrentUuid } from 'store/project-panel/project-panel-action';
+import {
+    IS_PROJECT_PANEL_TRASHED,
+    projectPanelActions,
+    getProjectPanelCurrentUuid
+} from 'store/project-panel/project-panel-action';
 import { Dispatch, MiddlewareAPI } from "redux";
 import { ProjectResource } from "models/project";
 import { updateResources } from "store/resources/resources-actions";
@@ -29,7 +33,10 @@ import { ListResults } from 'services/common-service/common-service';
 import { loadContainers } from 'store/processes/processes-actions';
 import { ResourceKind } from 'models/resource';
 import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
-import { serializeResourceTypeFilters, ProcessStatusFilter } from 'store/resource-type-filters/resource-type-filters';
+import {
+    serializeResourceTypeFilters,
+    buildProcessStatusFilters
+} from 'store/resource-type-filters/resource-type-filters';
 import { updatePublicFavorites } from 'store/public-favorites/public-favorites-actions';
 
 export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService {
@@ -116,27 +123,10 @@ export const getFilters = (dataExplorer: DataExplorer) => {
         .getFilters();
 
     // Filter by container status
-    const fb = new FilterBuilder();
-    switch (activeStatusFilter) {
-        case ProcessStatusFilter.COMPLETED: {
-            fb.addEqual('container.state', 'Complete', GroupContentsResourcePrefix.PROCESS);
-            fb.addEqual('container.exit_code', '0', GroupContentsResourcePrefix.PROCESS);
-            break;
-        }
-        case ProcessStatusFilter.FAILED: {
-            fb.addEqual('container.state', 'Complete', GroupContentsResourcePrefix.PROCESS);
-            fb.addDistinct('container.exit_code', '0', GroupContentsResourcePrefix.PROCESS);
-            break;
-        }
-        case ProcessStatusFilter.CANCELLED:
-        case ProcessStatusFilter.LOCKED:
-        case ProcessStatusFilter.QUEUED:
-        case ProcessStatusFilter.RUNNING: {
-            fb.addEqual('container.state', activeStatusFilter, GroupContentsResourcePrefix.PROCESS);
-            break;
-        }
-    }
-    const statusFilters = fb.getFilters();
+    const statusFilters = buildProcessStatusFilters(
+        new FilterBuilder(),
+        activeStatusFilter || '',
+        GroupContentsResourcePrefix.PROCESS).getFilters();
 
     return joinFilters(
         statusFilters,
index 71b00b2ed328ace73e8a0553adf9cb661bb2aeef..698515bde55f8fc945baa5ce3d9e40bddd22f623 100644 (file)
@@ -2,10 +2,29 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { getInitialResourceTypeFilters, serializeResourceTypeFilters, ObjectTypeFilter, CollectionTypeFilter, ProcessTypeFilter, GroupTypeFilter } from './resource-type-filters';
+import { getInitialResourceTypeFilters, serializeResourceTypeFilters, ObjectTypeFilter, CollectionTypeFilter, ProcessTypeFilter, GroupTypeFilter, buildProcessStatusFilters, ProcessStatusFilter } from './resource-type-filters';
 import { ResourceKind } from 'models/resource';
 import { deselectNode } from 'models/tree';
 import { pipe } from 'lodash/fp';
+import { FilterBuilder } from 'services/api/filter-builder';
+
+describe("buildProcessStatusFilters", () => {
+    [
+        [ProcessStatusFilter.ALL, ""],
+        [ProcessStatusFilter.ONHOLD, `["state","!=","Final"],["priority","=","0"],["container.state","in",["Queued","Locked"]]`],
+        [ProcessStatusFilter.COMPLETED, `["container.state","=","Complete"],["container.exit_code","=","0"]`],
+        [ProcessStatusFilter.FAILED, `["container.state","=","Complete"],["container.exit_code","!=","0"]`],
+        [ProcessStatusFilter.QUEUED, `["container.state","=","Queued"],["priority","!=","0"]`],
+        [ProcessStatusFilter.CANCELLED, `["container.state","=","Cancelled"]`],
+        [ProcessStatusFilter.RUNNING, `["container.state","=","Running"]`],
+    ].forEach(([status, expected]) => {
+        it(`can filter "${status}" processes`, () => {
+            const filters = buildProcessStatusFilters(new FilterBuilder(), status);
+            expect(filters.getFilters())
+                .toEqual(expected);
+        })
+    });
+});
 
 describe("serializeResourceTypeFilters", () => {
     it("should serialize all filters", () => {
index e42a16d89863e494c22ecef9fc87c8f0329eabae..a39807d58238196a44a9c93e347927778ad4bf36 100644 (file)
@@ -11,6 +11,7 @@ import { getSelectedNodes } from 'models/tree';
 import { CollectionType } from 'models/collection';
 import { GroupContentsResourcePrefix } from 'services/groups-service/groups-service';
 import { ContainerState } from 'models/container';
+import { ContainerRequestState } from 'models/container-request';
 
 export enum ProcessStatusFilter {
     ALL = 'All',
@@ -18,7 +19,7 @@ export enum ProcessStatusFilter {
     FAILED = 'Failed',
     COMPLETED = 'Completed',
     CANCELLED = 'Cancelled',
-    LOCKED = 'Locked',
+    ONHOLD = 'On hold',
     QUEUED = 'Queued'
 }
 
@@ -95,12 +96,12 @@ export const getInitialProcessStatusFilters = pipe(
     (): DataTableFilters => createTree<DataTableFilterItem>(),
     pipe(
         initFilter(ProcessStatusFilter.ALL, '', true),
+        initFilter(ProcessStatusFilter.ONHOLD, '', false),
+        initFilter(ProcessStatusFilter.QUEUED, '', false),
         initFilter(ProcessStatusFilter.RUNNING, '', false),
-        initFilter(ProcessStatusFilter.FAILED, '', false),
         initFilter(ProcessStatusFilter.COMPLETED, '', false),
         initFilter(ProcessStatusFilter.CANCELLED, '', false),
-        initFilter(ProcessStatusFilter.QUEUED, '', false),
-        initFilter(ProcessStatusFilter.LOCKED, '', false),
+        initFilter(ProcessStatusFilter.FAILED, '', false),
     ),
 );
 
@@ -272,27 +273,32 @@ export const serializeSimpleObjectTypeFilters = (filters: Tree<DataTableFilterIt
         .map(objectTypeToResourceKind);
 };
 
-export const buildProcessStatusFilters = ( fb:FilterBuilder, activeStatusFilter:string ): FilterBuilder => {
+export const buildProcessStatusFilters = ( fb: FilterBuilder, activeStatusFilter: string, resourcePrefix?: string ): FilterBuilder => {
     switch (activeStatusFilter) {
+        case ProcessStatusFilter.ONHOLD: {
+            fb.addDistinct('state', ContainerRequestState.FINAL, resourcePrefix);
+            fb.addEqual('priority', '0', resourcePrefix);
+            fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
+            break;
+        }
         case ProcessStatusFilter.COMPLETED: {
-            fb.addEqual('container.state', ContainerState.COMPLETE);
-            fb.addEqual('container.exit_code', '0');
+            fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
+            fb.addEqual('container.exit_code', '0', resourcePrefix);
             break;
         }
         case ProcessStatusFilter.FAILED: {
-            fb.addEqual('container.state', ContainerState.COMPLETE);
-            fb.addDistinct('container.exit_code', '0');
+            fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
+            fb.addDistinct('container.exit_code', '0', resourcePrefix);
             break;
         }
         case ProcessStatusFilter.QUEUED: {
-            fb.addEqual('container.state', ContainerState.QUEUED);
-            fb.addDistinct('container.priority', '0');
+            fb.addEqual('container.state', ContainerState.QUEUED, resourcePrefix);
+            fb.addDistinct('priority', '0', resourcePrefix);
             break;
         }
         case ProcessStatusFilter.CANCELLED:
-        case ProcessStatusFilter.LOCKED:
         case ProcessStatusFilter.RUNNING: {
-            fb.addEqual('container.state', activeStatusFilter);
+            fb.addEqual('container.state', activeStatusFilter, resourcePrefix);
             break;
         }
     }