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',
32 export enum GroupTypeFilter {
33 PROJECT = 'Project (normal)',
34 FILTER_GROUP = 'Filter group',
37 export enum CollectionTypeFilter {
38 GENERAL_COLLECTION = 'General',
39 OUTPUT_COLLECTION = 'Output',
40 LOG_COLLECTION = 'Log',
43 export enum ProcessTypeFilter {
44 MAIN_PROCESS = 'Main',
45 CHILD_PROCESS = 'Child',
48 const initFilter = (name: string, parent = '', isSelected?: boolean) =>
49 setNode<DataTableFilterItem>({
55 selected: isSelected !== undefined ? isSelected : true,
57 status: TreeNodeStatus.LOADED,
60 export const getSimpleObjectTypeFilters = pipe(
61 (): DataTableFilters => createTree<DataTableFilterItem>(),
62 initFilter(ObjectTypeFilter.PROJECT),
63 initFilter(ObjectTypeFilter.PROCESS),
64 initFilter(ObjectTypeFilter.COLLECTION),
67 // Using pipe() with more than 7 arguments makes the return type be 'any',
68 // causing compile issues.
69 export const getInitialResourceTypeFilters = pipe(
70 (): DataTableFilters => createTree<DataTableFilterItem>(),
72 initFilter(ObjectTypeFilter.PROJECT),
73 initFilter(GroupTypeFilter.PROJECT, ObjectTypeFilter.PROJECT),
74 initFilter(GroupTypeFilter.FILTER_GROUP, ObjectTypeFilter.PROJECT),
77 initFilter(ObjectTypeFilter.PROCESS),
78 initFilter(ProcessTypeFilter.MAIN_PROCESS, ObjectTypeFilter.PROCESS),
79 initFilter(ProcessTypeFilter.CHILD_PROCESS, ObjectTypeFilter.PROCESS)
82 initFilter(ObjectTypeFilter.COLLECTION),
83 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
84 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
85 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
89 export const getInitialProcessTypeFilters = pipe(
90 (): DataTableFilters => createTree<DataTableFilterItem>(),
91 initFilter(ProcessTypeFilter.MAIN_PROCESS),
92 initFilter(ProcessTypeFilter.CHILD_PROCESS, '', false)
95 export const getInitialProcessStatusFilters = pipe(
96 (): DataTableFilters => createTree<DataTableFilterItem>(),
98 initFilter(ProcessStatusFilter.ALL, '', true),
99 initFilter(ProcessStatusFilter.ONHOLD, '', false),
100 initFilter(ProcessStatusFilter.QUEUED, '', false),
101 initFilter(ProcessStatusFilter.RUNNING, '', false),
102 initFilter(ProcessStatusFilter.COMPLETED, '', false),
103 initFilter(ProcessStatusFilter.CANCELLED, '', false),
104 initFilter(ProcessStatusFilter.FAILED, '', false),
108 export const getTrashPanelTypeFilters = pipe(
109 (): DataTableFilters => createTree<DataTableFilterItem>(),
110 initFilter(ObjectTypeFilter.PROJECT),
111 initFilter(ObjectTypeFilter.COLLECTION),
112 initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
113 initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
114 initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
117 const createFiltersBuilder = (filters: DataTableFilters) =>
118 ({ fb: new FilterBuilder(), selectedFilters: getSelectedNodes(filters) });
120 const getMatchingFilters = (values: string[], filters: TreeNode<DataTableFilterItem>[]) =>
123 .filter(includes(__, values));
125 const objectTypeToResourceKind = (type: ObjectTypeFilter) => {
127 case ObjectTypeFilter.PROJECT:
128 return ResourceKind.PROJECT;
129 case ObjectTypeFilter.PROCESS:
130 return ResourceKind.PROCESS;
131 case ObjectTypeFilter.COLLECTION:
132 return ResourceKind.COLLECTION;
136 const serializeObjectTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => {
137 const groupFilters = getMatchingFilters(values(GroupTypeFilter), selectedFilters);
138 const collectionFilters = getMatchingFilters(values(CollectionTypeFilter), selectedFilters);
139 const processFilters = getMatchingFilters(values(ProcessTypeFilter), selectedFilters);
140 const typeFilters = pipe(
141 () => new Set(getMatchingFilters(values(ObjectTypeFilter), selectedFilters)),
142 set => groupFilters.length > 0
143 ? set.add(ObjectTypeFilter.PROJECT)
145 set => collectionFilters.length > 0
146 ? set.add(ObjectTypeFilter.COLLECTION)
148 set => processFilters.length > 0
149 ? set.add(ObjectTypeFilter.PROCESS)
151 set => Array.from(set)
155 fb: typeFilters.length > 0
156 ? fb.addIsA('uuid', typeFilters.map(objectTypeToResourceKind))
162 const collectionTypeToPropertyValue = (type: CollectionTypeFilter) => {
164 case CollectionTypeFilter.GENERAL_COLLECTION:
165 return CollectionType.GENERAL;
166 case CollectionTypeFilter.OUTPUT_COLLECTION:
167 return CollectionType.OUTPUT;
168 case CollectionTypeFilter.LOG_COLLECTION:
169 return CollectionType.LOG;
173 const serializeCollectionTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
174 () => getMatchingFilters(values(CollectionTypeFilter), selectedFilters),
175 filters => filters.map(collectionTypeToPropertyValue),
177 fb: buildCollectionTypeFilters({ fb, filters: mappedFilters }),
182 const COLLECTION_TYPES = values(CollectionType);
184 const NON_GENERAL_COLLECTION_TYPES = difference(COLLECTION_TYPES, [CollectionType.GENERAL]);
186 const COLLECTION_PROPERTIES_PREFIX = `${GroupContentsResourcePrefix.COLLECTION}.properties`;
188 const buildCollectionTypeFilters = ({ fb, filters }: { fb: FilterBuilder, filters: CollectionType[] }) => {
190 case filters.length === 0 || filters.length === COLLECTION_TYPES.length:
192 case includes(CollectionType.GENERAL, filters):
193 return fb.addNotIn('type', difference(NON_GENERAL_COLLECTION_TYPES, filters), COLLECTION_PROPERTIES_PREFIX);
195 return fb.addIn('type', filters, COLLECTION_PROPERTIES_PREFIX);
199 const serializeGroupTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
200 () => getMatchingFilters(values(GroupTypeFilter), selectedFilters),
203 fb: buildGroupTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
208 const GROUP_TYPES = values(GroupTypeFilter);
210 const buildGroupTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
212 case filters.length === 0 || filters.length === GROUP_TYPES.length:
214 case includes(GroupTypeFilter.PROJECT, filters):
215 return fb.addEqual('groups.group_class', 'project');
216 case includes(GroupTypeFilter.FILTER_GROUP, filters):
217 return fb.addEqual('groups.group_class', 'filter');
223 const serializeProcessTypeFilters = ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
224 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
227 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: true }),
232 const PROCESS_TYPES = values(ProcessTypeFilter);
233 const PROCESS_PREFIX = GroupContentsResourcePrefix.PROCESS;
235 const buildProcessTypeFilters = ({ fb, filters, use_prefix }: { fb: FilterBuilder, filters: string[], use_prefix: boolean }) => {
237 case filters.length === 0 || filters.length === PROCESS_TYPES.length:
239 case includes(ProcessTypeFilter.MAIN_PROCESS, filters):
240 return fb.addEqual('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
241 case includes(ProcessTypeFilter.CHILD_PROCESS, filters):
242 return fb.addDistinct('requesting_container_uuid', null, use_prefix ? PROCESS_PREFIX : '');
248 export const serializeResourceTypeFilters = pipe(
249 createFiltersBuilder,
250 serializeObjectTypeFilters,
251 serializeGroupTypeFilters,
252 serializeCollectionTypeFilters,
253 serializeProcessTypeFilters,
254 ({ fb }) => fb.getFilters(),
257 export const serializeOnlyProcessTypeFilters = pipe(
258 createFiltersBuilder,
259 ({ fb, selectedFilters }: ReturnType<typeof createFiltersBuilder>) => pipe(
260 () => getMatchingFilters(values(ProcessTypeFilter), selectedFilters),
263 fb: buildProcessTypeFilters({ fb, filters: mappedFilters, use_prefix: false }),
267 ({ fb }) => fb.getFilters(),
270 export const serializeSimpleObjectTypeFilters = (filters: Tree<DataTableFilterItem>) => {
271 return getSelectedNodes(filters)
273 .map(objectTypeToResourceKind);
276 export const buildProcessStatusFilters = ( fb: FilterBuilder, activeStatusFilter: string, resourcePrefix?: string ): FilterBuilder => {
277 switch (activeStatusFilter) {
278 case ProcessStatusFilter.ONHOLD: {
279 fb.addDistinct('state', ContainerRequestState.FINAL, resourcePrefix);
280 fb.addEqual('priority', '0', resourcePrefix);
281 fb.addIn('container.state', [ContainerState.QUEUED, ContainerState.LOCKED], resourcePrefix);
284 case ProcessStatusFilter.COMPLETED: {
285 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
286 fb.addEqual('container.exit_code', '0', resourcePrefix);
289 case ProcessStatusFilter.FAILED: {
290 fb.addEqual('container.state', ContainerState.COMPLETE, resourcePrefix);
291 fb.addDistinct('container.exit_code', '0', resourcePrefix);
294 case ProcessStatusFilter.QUEUED: {
295 fb.addEqual('container.state', ContainerState.QUEUED, resourcePrefix);
296 fb.addDistinct('priority', '0', resourcePrefix);
299 case ProcessStatusFilter.CANCELLED:
300 case ProcessStatusFilter.RUNNING: {
301 fb.addEqual('container.state', activeStatusFilter, resourcePrefix);