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 {
29 COLLECTION = 'Data collection',
30 WORKFLOW = 'Workflow',
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',
44 export enum ProcessTypeFilter {
45 MAIN_PROCESS = 'Main',
46 CHILD_PROCESS = 'Child',
49 const initFilter = (name: string, parent = '', isSelected?: boolean) =>
50 setNode<DataTableFilterItem>({
56 selected: isSelected !== undefined ? isSelected : true,
58 status: TreeNodeStatus.LOADED,
61 export const getSimpleObjectTypeFilters = pipe(
62 (): DataTableFilters => createTree<DataTableFilterItem>(),
63 initFilter(ObjectTypeFilter.PROJECT),
64 initFilter(ObjectTypeFilter.PROCESS),
65 initFilter(ObjectTypeFilter.COLLECTION),
66 initFilter(ObjectTypeFilter.WORKFLOW),
69 // Using pipe() with more than 7 arguments makes the return type be 'any',
70 // causing compile issues.
71 export const getInitialResourceTypeFilters = pipe(
72 (): DataTableFilters => createTree<DataTableFilterItem>(),
74 initFilter(ObjectTypeFilter.PROJECT),
75 initFilter(GroupTypeFilter.PROJECT, ObjectTypeFilter.PROJECT),
76 initFilter(GroupTypeFilter.FILTER_GROUP, ObjectTypeFilter.PROJECT),
79 initFilter(ObjectTypeFilter.PROCESS),
80 initFilter(ProcessTypeFilter.MAIN_PROCESS, ObjectTypeFilter.PROCESS),
81 initFilter(ProcessTypeFilter.CHILD_PROCESS, ObjectTypeFilter.PROCESS)
84 initFilter(ObjectTypeFilter.COLLECTION),
85 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
86 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
87 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
89 initFilter(ObjectTypeFilter.WORKFLOW)
93 export const getInitialProcessTypeFilters = pipe(
94 (): DataTableFilters => createTree<DataTableFilterItem>(),
95 initFilter(ProcessTypeFilter.MAIN_PROCESS),
96 initFilter(ProcessTypeFilter.CHILD_PROCESS, '', false)
99 export const getInitialProcessStatusFilters = pipe(
100 (): DataTableFilters => createTree<DataTableFilterItem>(),
102 initFilter(ProcessStatusFilter.ALL, '', true),
103 initFilter(ProcessStatusFilter.ONHOLD, '', false),
104 initFilter(ProcessStatusFilter.QUEUED, '', false),
105 initFilter(ProcessStatusFilter.RUNNING, '', false),
106 initFilter(ProcessStatusFilter.COMPLETED, '', false),
107 initFilter(ProcessStatusFilter.CANCELLED, '', false),
108 initFilter(ProcessStatusFilter.FAILED, '', false),
112 export const getTrashPanelTypeFilters = pipe(
113 (): DataTableFilters => createTree<DataTableFilterItem>(),
114 initFilter(ObjectTypeFilter.PROJECT),
115 initFilter(ObjectTypeFilter.COLLECTION),
116 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
117 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
118 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
121 const createFiltersBuilder = (filters: DataTableFilters) =>
122 ({ fb: new FilterBuilder(), selectedFilters: getSelectedNodes(filters) });
124 const getMatchingFilters = (values: string[], filters: TreeNode<DataTableFilterItem>[]) =>
127 .filter(includes(__, values));
129 const objectTypeToResourceKind = (type: ObjectTypeFilter) => {
131 case ObjectTypeFilter.PROJECT:
132 return ResourceKind.PROJECT;
133 case ObjectTypeFilter.PROCESS:
134 return ResourceKind.PROCESS;
135 case ObjectTypeFilter.COLLECTION:
136 return ResourceKind.COLLECTION;
137 case ObjectTypeFilter.WORKFLOW:
138 return ResourceKind.WORKFLOW;
142 const serializeObjectTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => {
143 const groupFilters = getMatchingFilters(values(GroupTypeFilter), selectedFilters);
144 const collectionFilters = getMatchingFilters(values(CollectionTypeFilter), selectedFilters);
145 const processFilters = getMatchingFilters(values(ProcessTypeFilter), selectedFilters);
146 const typeFilters = pipe(
147 () => new Set(getMatchingFilters(values(ObjectTypeFilter), selectedFilters)),
148 set => groupFilters.length > 0
149 ? set.add(ObjectTypeFilter.PROJECT)
151 set => collectionFilters.length > 0
152 ? set.add(ObjectTypeFilter.COLLECTION)
154 set => processFilters.length > 0
155 ? set.add(ObjectTypeFilter.PROCESS)
157 set => Array.from(set)
161 fb: typeFilters.length > 0
162 ? fb.addIsA('uuid', typeFilters.map(objectTypeToResourceKind))
168 const collectionTypeToPropertyValue = (type: CollectionTypeFilter) => {
170 case CollectionTypeFilter.GENERAL_COLLECTION:
171 return CollectionType.GENERAL;
172 case CollectionTypeFilter.OUTPUT_COLLECTION:
173 return CollectionType.OUTPUT;
174 case CollectionTypeFilter.LOG_COLLECTION:
175 return CollectionType.LOG;
179 const serializeCollectionTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
180 () => getMatchingFilters(values(CollectionTypeFilter), selectedFilters),
181 filters => filters.map(collectionTypeToPropertyValue),
183 fb: buildCollectionTypeFilters({ fb, filters: mappedFilters }),
188 const COLLECTION_TYPES = values(CollectionType);
190 const NON_GENERAL_COLLECTION_TYPES = difference(COLLECTION_TYPES, [CollectionType.GENERAL]);
192 const COLLECTION_PROPERTIES_PREFIX = `${GroupContentsResourcePrefix.COLLECTION}.properties`;
194 const buildCollectionTypeFilters = ({ fb, filters }: { fb: FilterBuilder, filters: CollectionType[] }) => {
196 case filters.length === 0 || filters.length === COLLECTION_TYPES.length:
198 case includes(CollectionType.GENERAL, filters):
199 return fb.addNotIn('type', difference(NON_GENERAL_COLLECTION_TYPES, filters), COLLECTION_PROPERTIES_PREFIX);
201 return fb.addIn('type', filters, COLLECTION_PROPERTIES_PREFIX);
205 const serializeGroupTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
206 () => getMatchingFilters(values(GroupTypeFilter), selectedFilters),
209 fb: buildGroupTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
214 const GROUP_TYPES = values(GroupTypeFilter);
216 const buildGroupTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
218 case filters.length === 0 || filters.length === GROUP_TYPES.length:
220 case includes(GroupTypeFilter.PROJECT, filters):
221 return fb.addEqual('groups.group_class', 'project');
222 case includes(GroupTypeFilter.FILTER_GROUP, filters):
223 return fb.addEqual('groups.group_class', 'filter');
229 const serializeProcessTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
230 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
233 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
238 const PROCESS_TYPES = values(ProcessTypeFilter);
239 const PROCESS_PREFIX = GroupContentsResourcePrefix.PROCESS;
241 const buildProcessTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
243 case filters.length === 0 || filters.length === PROCESS_TYPES.length:
245 case includes(ProcessTypeFilter.MAIN_PROCESS, filters):
246 return fb.addEqual('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
247 case includes(ProcessTypeFilter.CHILD_PROCESS, filters):
248 return fb.addDistinct('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
254 export const serializeResourceTypeFilters = pipe(
255 createFiltersBuilder,
256 serializeObjectTypeFilters,
257 serializeGroupTypeFilters,
258 serializeCollectionTypeFilters,
259 serializeProcessTypeFilters,
260 ({ fb }) => fb.getFilters(),
263 export const serializeOnlyProcessTypeFilters = pipe(
264 createFiltersBuilder,
265 ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
266 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
269 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: false }),
273 ({ fb }) => fb.getFilters(),
276 export const serializeSimpleObjectTypeFilters = (filters: Tree<DataTableFilterItem>) => {
277 return getSelectedNodes(filters)
279 .map(objectTypeToResourceKind);
282 export const buildProcessStatusFilters = (fb: FilterBuilder, activeStatusFilter: string, resourcePrefix?: string): FilterBuilder => {
283 switch (activeStatusFilter) {
284 case ProcessStatusFilter.ONHOLD: {
285 fb.addDistinct('state', ContainerRequestState.FINAL, resourcePrefix);
286 fb.addEqual('priority', '0', resourcePrefix);
287 fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
290 case ProcessStatusFilter.COMPLETED: {
291 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
292 fb.addEqual('container.exit_code', '0', resourcePrefix);
295 case ProcessStatusFilter.FAILED: {
296 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
297 fb.addDistinct('container.exit_code', '0', resourcePrefix);
300 case ProcessStatusFilter.QUEUED: {
301 fb.addEqual('container.state', ContainerState.QUEUED, resourcePrefix);
302 fb.addDistinct('priority', '0', resourcePrefix);
305 case ProcessStatusFilter.CANCELLED:
306 case ProcessStatusFilter.RUNNING: {
307 fb.addEqual('container.state', activeStatusFilter, resourcePrefix);