1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { difference, pipe, values, includes, __ } from 'lodash/fp';
6 import { createTree, setNode, TreeNodeStatus, TreeNode, Tree } from 'models/tree';
7 import { DataTableFilterItem, DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
8 import { ResourceKind } from 'models/resource';
9 import { FilterBuilder } from 'services/api/filter-builder';
10 import { getSelectedNodes } from 'models/tree';
11 import { CollectionType } from 'models/collection';
12 import { GroupContentsResourcePrefix } from 'services/groups-service/groups-service';
13 import { ContainerState } from 'models/container';
14 import { ContainerRequestState } from 'models/container-request';
16 export enum ProcessStatusFilter {
20 COMPLETED = 'Completed',
21 CANCELLED = 'Cancelled',
26 export enum ObjectTypeFilter {
28 WORKFLOW = 'Workflow',
29 COLLECTION = 'Data collection',
30 DEFINITION = 'Definition',
33 export enum GroupTypeFilter {
34 PROJECT = 'Project (normal)',
35 FILTER_GROUP = 'Filter group',
38 export enum CollectionTypeFilter {
39 GENERAL_COLLECTION = 'General',
40 OUTPUT_COLLECTION = 'Output',
41 LOG_COLLECTION = 'Log',
42 INTERMEDIATE_COLLECTION = 'Intermediate',
45 export enum ProcessTypeFilter {
46 MAIN_PROCESS = 'Runs',
47 CHILD_PROCESS = 'Intermediate Steps',
50 const initFilter = (name: string, parent = '', isSelected?: boolean, isExpanded?: boolean) =>
51 setNode<DataTableFilterItem>({
57 selected: isSelected !== undefined ? isSelected : true,
58 initialState: isSelected !== undefined ? isSelected : true,
59 expanded: isExpanded !== undefined ? isExpanded : false,
60 status: TreeNodeStatus.LOADED,
63 export const getSimpleObjectTypeFilters = pipe(
64 (): DataTableFilters => createTree<DataTableFilterItem>(),
65 initFilter(ObjectTypeFilter.PROJECT),
66 initFilter(ObjectTypeFilter.WORKFLOW),
67 initFilter(ObjectTypeFilter.COLLECTION),
68 initFilter(ObjectTypeFilter.DEFINITION),
71 // Using pipe() with more than 7 arguments makes the return type be 'any',
72 // causing compile issues.
73 export const getInitialResourceTypeFilters = pipe(
74 (): DataTableFilters => createTree<DataTableFilterItem>(),
76 initFilter(ObjectTypeFilter.PROJECT, '', true, true),
77 initFilter(GroupTypeFilter.PROJECT, ObjectTypeFilter.PROJECT),
78 initFilter(GroupTypeFilter.FILTER_GROUP, ObjectTypeFilter.PROJECT),
81 initFilter(ObjectTypeFilter.WORKFLOW, '', false, true),
82 initFilter(ObjectTypeFilter.DEFINITION, ObjectTypeFilter.WORKFLOW),
83 initFilter(ProcessTypeFilter.MAIN_PROCESS, ObjectTypeFilter.WORKFLOW),
84 initFilter(ProcessTypeFilter.CHILD_PROCESS, ObjectTypeFilter.WORKFLOW, false),
87 initFilter(ObjectTypeFilter.COLLECTION, '', true, true),
88 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
89 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
90 initFilter(CollectionTypeFilter.INTERMEDIATE_COLLECTION, ObjectTypeFilter.COLLECTION),
91 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
96 export const getInitialProcessTypeFilters = pipe(
97 (): DataTableFilters => createTree<DataTableFilterItem>(),
98 initFilter(ProcessTypeFilter.MAIN_PROCESS),
99 initFilter(ProcessTypeFilter.CHILD_PROCESS, '', false)
102 export const getInitialProcessStatusFilters = pipe(
103 (): DataTableFilters => createTree<DataTableFilterItem>(),
105 initFilter(ProcessStatusFilter.ALL, '', true),
106 initFilter(ProcessStatusFilter.ONHOLD, '', false),
107 initFilter(ProcessStatusFilter.QUEUED, '', false),
108 initFilter(ProcessStatusFilter.RUNNING, '', false),
109 initFilter(ProcessStatusFilter.COMPLETED, '', false),
110 initFilter(ProcessStatusFilter.CANCELLED, '', false),
111 initFilter(ProcessStatusFilter.FAILED, '', false),
115 export const getTrashPanelTypeFilters = pipe(
116 (): DataTableFilters => createTree<DataTableFilterItem>(),
117 initFilter(ObjectTypeFilter.PROJECT),
118 initFilter(ObjectTypeFilter.COLLECTION),
119 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
120 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
121 initFilter(CollectionTypeFilter.INTERMEDIATE_COLLECTION, ObjectTypeFilter.COLLECTION),
122 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
125 const createFiltersBuilder = (filters: DataTableFilters) =>
126 ({ fb: new FilterBuilder(), selectedFilters: getSelectedNodes(filters) });
128 const getMatchingFilters = (values: string[], filters: TreeNode<DataTableFilterItem>[]) =>
131 .filter(includes(__, values));
133 const objectTypeToResourceKind = (type: ObjectTypeFilter) => {
135 case ObjectTypeFilter.PROJECT:
136 return ResourceKind.PROJECT;
137 case ObjectTypeFilter.WORKFLOW:
138 return ResourceKind.PROCESS;
139 case ObjectTypeFilter.COLLECTION:
140 return ResourceKind.COLLECTION;
141 case ObjectTypeFilter.DEFINITION:
142 return ResourceKind.WORKFLOW;
146 const serializeObjectTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => {
147 const groupFilters = getMatchingFilters(values(GroupTypeFilter), selectedFilters);
148 const collectionFilters = getMatchingFilters(values(CollectionTypeFilter), selectedFilters);
149 const processFilters = getMatchingFilters(values(ProcessTypeFilter), selectedFilters);
150 const typeFilters = pipe(
151 () => new Set(getMatchingFilters(values(ObjectTypeFilter), selectedFilters)),
152 set => groupFilters.length > 0
153 ? set.add(ObjectTypeFilter.PROJECT)
155 set => collectionFilters.length > 0
156 ? set.add(ObjectTypeFilter.COLLECTION)
158 set => processFilters.length > 0
159 ? set.add(ObjectTypeFilter.WORKFLOW)
161 set => Array.from(set)
165 fb: typeFilters.length > 0
166 ? fb.addIsA('uuid', typeFilters.map(objectTypeToResourceKind))
167 : fb.addIsA('uuid', ResourceKind.NONE),
172 const collectionTypeToPropertyValue = (type: CollectionTypeFilter) => {
174 case CollectionTypeFilter.GENERAL_COLLECTION:
175 return CollectionType.GENERAL;
176 case CollectionTypeFilter.OUTPUT_COLLECTION:
177 return CollectionType.OUTPUT;
178 case CollectionTypeFilter.LOG_COLLECTION:
179 return CollectionType.LOG;
180 case CollectionTypeFilter.INTERMEDIATE_COLLECTION:
181 return CollectionType.INTERMEDIATE;
183 return CollectionType.GENERAL;
187 const serializeCollectionTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
188 () => getMatchingFilters(values(CollectionTypeFilter), selectedFilters),
189 filters => filters.map(collectionTypeToPropertyValue),
191 fb: buildCollectionTypeFilters({ fb, filters: mappedFilters }),
196 const COLLECTION_TYPES = values(CollectionType);
198 const NON_GENERAL_COLLECTION_TYPES = difference(COLLECTION_TYPES, [CollectionType.GENERAL]);
200 const COLLECTION_PROPERTIES_PREFIX = `${GroupContentsResourcePrefix.COLLECTION}.properties`;
202 const buildCollectionTypeFilters = ({ fb, filters }: { fb: FilterBuilder, filters: CollectionType[] }) => {
204 case filters.length === 0 || filters.length === COLLECTION_TYPES.length:
206 case includes(CollectionType.GENERAL, filters):
207 return fb.addNotIn('type', difference(NON_GENERAL_COLLECTION_TYPES, filters), COLLECTION_PROPERTIES_PREFIX);
209 return fb.addIn('type', filters, COLLECTION_PROPERTIES_PREFIX);
213 const serializeGroupTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
214 () => getMatchingFilters(values(GroupTypeFilter), selectedFilters),
217 fb: buildGroupTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
222 const GROUP_TYPES = values(GroupTypeFilter);
224 const buildGroupTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
226 case filters.length === 0 || filters.length === GROUP_TYPES.length:
228 case includes(GroupTypeFilter.PROJECT, filters):
229 return fb.addEqual('groups.group_class', 'project');
230 case includes(GroupTypeFilter.FILTER_GROUP, filters):
231 return fb.addEqual('groups.group_class', 'filter');
237 const serializeProcessTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
238 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
241 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
246 const PROCESS_TYPES = values(ProcessTypeFilter);
247 const PROCESS_PREFIX = GroupContentsResourcePrefix.PROCESS;
249 const buildProcessTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
251 case filters.length === 0 || filters.length === PROCESS_TYPES.length:
253 case includes(ProcessTypeFilter.MAIN_PROCESS, filters):
254 return fb.addEqual('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
255 case includes(ProcessTypeFilter.CHILD_PROCESS, filters):
256 return fb.addDistinct('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
262 export const serializeResourceTypeFilters = pipe(
263 createFiltersBuilder,
264 serializeObjectTypeFilters,
265 serializeGroupTypeFilters,
266 serializeCollectionTypeFilters,
267 serializeProcessTypeFilters,
268 ({ fb }) => fb.getFilters(),
271 export const serializeOnlyProcessTypeFilters = pipe(
272 createFiltersBuilder,
273 ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
274 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
277 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: false }),
281 ({ fb }) => fb.getFilters(),
284 export const serializeSimpleObjectTypeFilters = (filters: Tree<DataTableFilterItem>) => {
285 return getSelectedNodes(filters)
287 .map(objectTypeToResourceKind);
290 export const buildProcessStatusFilters = (fb: FilterBuilder, activeStatusFilter: string, resourcePrefix?: string): FilterBuilder => {
291 switch (activeStatusFilter) {
292 case ProcessStatusFilter.ONHOLD: {
293 fb.addDistinct('state', ContainerRequestState.FINAL, resourcePrefix);
294 fb.addEqual('priority', '0', resourcePrefix);
295 fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
298 case ProcessStatusFilter.COMPLETED: {
299 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
300 fb.addEqual('container.exit_code', '0', resourcePrefix);
303 case ProcessStatusFilter.FAILED: {
304 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
305 fb.addDistinct('container.exit_code', '0', resourcePrefix);
308 case ProcessStatusFilter.QUEUED: {
309 fb.addEqual('container.state', ContainerState.QUEUED, resourcePrefix);
310 fb.addDistinct('priority', '0', resourcePrefix);
313 case ProcessStatusFilter.CANCELLED:
314 case ProcessStatusFilter.RUNNING: {
315 fb.addEqual('container.state', activeStatusFilter, resourcePrefix);