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(ProcessTypeFilter.MAIN_PROCESS, ObjectTypeFilter.WORKFLOW),
83 initFilter(ProcessTypeFilter.CHILD_PROCESS, ObjectTypeFilter.WORKFLOW, false),
84 initFilter(ObjectTypeFilter.DEFINITION, ObjectTypeFilter.WORKFLOW),
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, false),
91 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION, false),
96 // Using pipe() with more than 7 arguments makes the return type be 'any',
97 // causing compile issues.
98 export const getInitialSearchTypeFilters = pipe(
99 (): DataTableFilters => createTree<DataTableFilterItem>(),
101 initFilter(ObjectTypeFilter.PROJECT, '', true, true),
102 initFilter(GroupTypeFilter.PROJECT, ObjectTypeFilter.PROJECT),
103 initFilter(GroupTypeFilter.FILTER_GROUP, ObjectTypeFilter.PROJECT),
106 initFilter(ObjectTypeFilter.WORKFLOW, '', false, true),
107 initFilter(ProcessTypeFilter.MAIN_PROCESS, ObjectTypeFilter.WORKFLOW, false),
108 initFilter(ProcessTypeFilter.CHILD_PROCESS, ObjectTypeFilter.WORKFLOW, false),
109 initFilter(ObjectTypeFilter.DEFINITION, ObjectTypeFilter.WORKFLOW, false),
112 initFilter(ObjectTypeFilter.COLLECTION, '', true, true),
113 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
114 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
115 initFilter(CollectionTypeFilter.INTERMEDIATE_COLLECTION, ObjectTypeFilter.COLLECTION, false),
116 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION, false),
120 export const getInitialProcessTypeFilters = pipe(
121 (): DataTableFilters => createTree<DataTableFilterItem>(),
122 initFilter(ProcessTypeFilter.MAIN_PROCESS),
123 initFilter(ProcessTypeFilter.CHILD_PROCESS, '', false)
126 export const getInitialProcessStatusFilters = pipe(
127 (): DataTableFilters => createTree<DataTableFilterItem>(),
129 initFilter(ProcessStatusFilter.ALL, '', true),
130 initFilter(ProcessStatusFilter.ONHOLD, '', false),
131 initFilter(ProcessStatusFilter.QUEUED, '', false),
132 initFilter(ProcessStatusFilter.RUNNING, '', false),
133 initFilter(ProcessStatusFilter.COMPLETED, '', false),
134 initFilter(ProcessStatusFilter.CANCELLED, '', false),
135 initFilter(ProcessStatusFilter.FAILED, '', false),
139 export const getTrashPanelTypeFilters = pipe(
140 (): DataTableFilters => createTree<DataTableFilterItem>(),
141 initFilter(ObjectTypeFilter.PROJECT),
142 initFilter(ObjectTypeFilter.COLLECTION),
143 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
144 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
145 initFilter(CollectionTypeFilter.INTERMEDIATE_COLLECTION, ObjectTypeFilter.COLLECTION),
146 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
149 const createFiltersBuilder = (filters: DataTableFilters) =>
150 ({ fb: new FilterBuilder(), selectedFilters: getSelectedNodes(filters) });
152 const getMatchingFilters = (values: string[], filters: TreeNode<DataTableFilterItem>[]) =>
155 .filter(includes(__, values));
157 const objectTypeToResourceKind = (type: ObjectTypeFilter) => {
159 case ObjectTypeFilter.PROJECT:
160 return ResourceKind.PROJECT;
161 case ObjectTypeFilter.WORKFLOW:
162 return ResourceKind.PROCESS;
163 case ObjectTypeFilter.COLLECTION:
164 return ResourceKind.COLLECTION;
165 case ObjectTypeFilter.DEFINITION:
166 return ResourceKind.WORKFLOW;
170 const serializeObjectTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => {
171 const groupFilters = getMatchingFilters(values(GroupTypeFilter), selectedFilters);
172 const collectionFilters = getMatchingFilters(values(CollectionTypeFilter), selectedFilters);
173 const processFilters = getMatchingFilters(values(ProcessTypeFilter), selectedFilters);
174 const typeFilters = pipe(
175 () => new Set(getMatchingFilters(values(ObjectTypeFilter), selectedFilters)),
176 set => groupFilters.length > 0
177 ? set.add(ObjectTypeFilter.PROJECT)
179 set => collectionFilters.length > 0
180 ? set.add(ObjectTypeFilter.COLLECTION)
182 set => processFilters.length > 0
183 ? set.add(ObjectTypeFilter.WORKFLOW)
185 set => Array.from(set)
189 fb: typeFilters.length > 0
190 ? fb.addIsA('uuid', typeFilters.map(objectTypeToResourceKind))
191 : fb.addIsA('uuid', ResourceKind.NONE),
196 const collectionTypeToPropertyValue = (type: CollectionTypeFilter) => {
198 case CollectionTypeFilter.GENERAL_COLLECTION:
199 return CollectionType.GENERAL;
200 case CollectionTypeFilter.OUTPUT_COLLECTION:
201 return CollectionType.OUTPUT;
202 case CollectionTypeFilter.LOG_COLLECTION:
203 return CollectionType.LOG;
204 case CollectionTypeFilter.INTERMEDIATE_COLLECTION:
205 return CollectionType.INTERMEDIATE;
207 return CollectionType.GENERAL;
211 const serializeCollectionTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
212 () => getMatchingFilters(values(CollectionTypeFilter), selectedFilters),
213 filters => filters.map(collectionTypeToPropertyValue),
215 fb: buildCollectionTypeFilters({ fb, filters: mappedFilters }),
220 const COLLECTION_TYPES = values(CollectionType);
222 const NON_GENERAL_COLLECTION_TYPES = difference(COLLECTION_TYPES, [CollectionType.GENERAL]);
224 const COLLECTION_PROPERTIES_PREFIX = `${GroupContentsResourcePrefix.COLLECTION}.properties`;
226 const buildCollectionTypeFilters = ({ fb, filters }: { fb: FilterBuilder, filters: CollectionType[] }) => {
228 case filters.length === 0 || filters.length === COLLECTION_TYPES.length:
230 case includes(CollectionType.GENERAL, filters):
231 return fb.addNotIn('type', difference(NON_GENERAL_COLLECTION_TYPES, filters), COLLECTION_PROPERTIES_PREFIX);
233 return fb.addIn('type', filters, COLLECTION_PROPERTIES_PREFIX);
237 const serializeGroupTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
238 () => getMatchingFilters(values(GroupTypeFilter), selectedFilters),
241 fb: buildGroupTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
246 const GROUP_TYPES = values(GroupTypeFilter);
248 const buildGroupTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
250 case filters.length === 0 || filters.length === GROUP_TYPES.length:
252 case includes(GroupTypeFilter.PROJECT, filters):
253 return fb.addEqual('groups.group_class', 'project');
254 case includes(GroupTypeFilter.FILTER_GROUP, filters):
255 return fb.addEqual('groups.group_class', 'filter');
261 const serializeProcessTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
262 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
265 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
270 const PROCESS_TYPES = values(ProcessTypeFilter);
271 const PROCESS_PREFIX = GroupContentsResourcePrefix.PROCESS;
273 const buildProcessTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
275 case filters.length === 0 || filters.length === PROCESS_TYPES.length:
277 case includes(ProcessTypeFilter.MAIN_PROCESS, filters):
278 return fb.addEqual('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
279 case includes(ProcessTypeFilter.CHILD_PROCESS, filters):
280 return fb.addDistinct('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
286 export const serializeResourceTypeFilters = pipe(
287 createFiltersBuilder,
288 serializeObjectTypeFilters,
289 serializeGroupTypeFilters,
290 serializeCollectionTypeFilters,
291 serializeProcessTypeFilters,
292 ({ fb }) => fb.getFilters(),
295 export const serializeOnlyProcessTypeFilters = pipe(
296 createFiltersBuilder,
297 ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
298 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
301 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: false }),
305 ({ fb }) => fb.getFilters(),
308 export const serializeSimpleObjectTypeFilters = (filters: Tree<DataTableFilterItem>) => {
309 return getSelectedNodes(filters)
311 .map(objectTypeToResourceKind);
314 export const buildProcessStatusFilters = (fb: FilterBuilder, activeStatusFilter: string, resourcePrefix?: string): FilterBuilder => {
315 switch (activeStatusFilter) {
316 case ProcessStatusFilter.ONHOLD: {
317 fb.addDistinct('state', ContainerRequestState.FINAL, resourcePrefix);
318 fb.addEqual('priority', '0', resourcePrefix);
319 fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
322 case ProcessStatusFilter.COMPLETED: {
323 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
324 fb.addEqual('container.exit_code', '0', resourcePrefix);
327 case ProcessStatusFilter.FAILED: {
328 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
329 fb.addDistinct('container.exit_code', '0', resourcePrefix);
332 case ProcessStatusFilter.QUEUED: {
333 fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
334 fb.addDistinct('priority', '0', resourcePrefix);
337 case ProcessStatusFilter.CANCELLED:
338 case ProcessStatusFilter.RUNNING: {
339 fb.addEqual('container.state', activeStatusFilter, resourcePrefix);