1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from 'redux';
6 import { RootState } from 'store/store';
7 import { getUserUuid } from "common/getuser";
8 import { getResource } from 'store/resources/resources';
9 import { propertiesActions } from '../properties/properties-actions';
10 import { getProcess } from 'store/processes/process';
11 import { ServiceRepository } from 'services/services';
12 import { SidePanelTreeCategory, activateSidePanelTreeItem } from 'store/side-panel-tree/side-panel-tree-actions';
13 import { updateResources } from '../resources/resources-actions';
14 import { ResourceKind } from 'models/resource';
15 import { GroupResource } from 'models/group';
16 import { extractUuidKind } from 'models/resource';
17 import { UserResource } from 'models/user';
18 import { FilterBuilder } from 'services/api/filter-builder';
19 import { ProcessResource } from 'models/process';
20 import { OrderBuilder } from 'services/api/order-builder';
21 import { Breadcrumb } from 'components/breadcrumbs/breadcrumbs';
22 import { ContainerRequestResource, containerRequestFieldsNoMounts } from 'models/container-request';
23 import { CollectionIcon, IconType, ProcessIcon, ProjectIcon, WorkflowIcon } from 'components/icon/icon';
24 import { CollectionResource } from 'models/collection';
25 import { getSidePanelIcon } from 'views-components/side-panel-tree/side-panel-tree';
26 import { WorkflowResource } from 'models/workflow';
28 export const BREADCRUMBS = 'breadcrumbs';
30 export const setBreadcrumbs = (breadcrumbs: any, currentItem?: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource) => {
32 breadcrumbs.push(resourceToBreadcrumb(currentItem));
34 return propertiesActions.SET_PROPERTY({ key: BREADCRUMBS, value: breadcrumbs });
37 const resourceToBreadcrumbIcon = (resource: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource): IconType | undefined => {
38 switch (resource.kind) {
39 case ResourceKind.PROJECT:
41 case ResourceKind.PROCESS:
43 case ResourceKind.COLLECTION:
44 return CollectionIcon;
45 case ResourceKind.WORKFLOW:
52 const resourceToBreadcrumb = (resource: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource): Breadcrumb => ({
55 icon: resourceToBreadcrumbIcon(resource),
58 export const setSidePanelBreadcrumbs = (uuid: string) =>
59 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
60 const ancestors = await services.ancestorsService.ancestors(uuid, '');
61 dispatch(updateResources(ancestors));
63 let breadcrumbs: Breadcrumb[] = [];
64 const { collectionPanel: { item } } = getState();
66 const path = getState().router.location!.pathname;
67 const currentUuid = path.split('/')[2];
68 const uuidKind = extractUuidKind(currentUuid);
69 const rootUuid = getUserUuid(getState());
71 if (ancestors.find(ancestor => ancestor.uuid === rootUuid)) {
72 // Handle home project uuid root
74 label: SidePanelTreeCategory.PROJECTS,
75 uuid: SidePanelTreeCategory.PROJECTS,
76 icon: getSidePanelIcon(SidePanelTreeCategory.PROJECTS)
78 } else if (Object.values(SidePanelTreeCategory).includes(uuid as SidePanelTreeCategory)) {
79 // Handle SidePanelTreeCategory root
83 icon: getSidePanelIcon(uuid)
87 breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) =>
88 ancestor.kind === ResourceKind.GROUP
89 ? [...breadcrumbs, resourceToBreadcrumb(ancestor)]
93 if (uuidKind === ResourceKind.COLLECTION) {
94 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
95 const parentProcessItem = await getCollectionParent(collectionItem)(services);
96 if (parentProcessItem) {
97 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
98 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
99 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
101 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
102 } else if (uuidKind === ResourceKind.PROCESS) {
103 const processItem = await services.containerRequestService.get(currentUuid);
104 const parentProcessItem = await getProcessParent(processItem)(services);
105 if (parentProcessItem) {
106 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
108 dispatch(setBreadcrumbs(breadcrumbs, processItem));
109 } else if (uuidKind === ResourceKind.WORKFLOW) {
110 const workflowItem = await services.workflowService.get(currentUuid);
111 dispatch(setBreadcrumbs(breadcrumbs, workflowItem));
113 dispatch(setBreadcrumbs(breadcrumbs));
116 export const setSharedWithMeBreadcrumbs = (uuid: string) =>
117 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.SHARED_WITH_ME);
119 export const setTrashBreadcrumbs = (uuid: string) =>
120 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.TRASH);
122 export const setCategoryBreadcrumbs = (uuid: string, category: string) =>
123 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
124 const ancestors = await services.ancestorsService.ancestors(uuid, '');
125 dispatch(updateResources(ancestors));
126 const initialBreadcrumbs: Breadcrumb[] = [
130 icon: getSidePanelIcon(category)
133 const { collectionPanel: { item } } = getState();
134 const path = getState().router.location!.pathname;
135 const currentUuid = path.split('/')[2];
136 const uuidKind = extractUuidKind(currentUuid);
137 let breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) =>
138 ancestor.kind === ResourceKind.GROUP
139 ? [...breadcrumbs, resourceToBreadcrumb(ancestor)]
142 if (uuidKind === ResourceKind.COLLECTION) {
143 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
144 const parentProcessItem = await getCollectionParent(collectionItem)(services);
145 if (parentProcessItem) {
146 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
147 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
148 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
150 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
151 } else if (uuidKind === ResourceKind.PROCESS) {
152 const processItem = await services.containerRequestService.get(currentUuid);
153 const parentProcessItem = await getProcessParent(processItem)(services);
154 if (parentProcessItem) {
155 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
157 dispatch(setBreadcrumbs(breadcrumbs, processItem));
158 } else if (uuidKind === ResourceKind.WORKFLOW) {
159 const workflowItem = await services.workflowService.get(currentUuid);
160 dispatch(setBreadcrumbs(breadcrumbs, workflowItem));
162 dispatch(setBreadcrumbs(breadcrumbs));
165 const getProcessParent = (childProcess: ContainerRequestResource) =>
166 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
167 if (childProcess.requestingContainerUuid) {
168 const parentProcesses = await services.containerRequestService.list({
169 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
170 filters: new FilterBuilder().addEqual('container_uuid', childProcess.requestingContainerUuid).getFilters(),
171 select: containerRequestFieldsNoMounts,
173 if (parentProcesses.items.length > 0) {
174 return parentProcesses.items[0];
183 const getCollectionParent = (collection: CollectionResource) =>
184 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
185 const parentOutputPromise = services.containerRequestService.list({
186 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
187 filters: new FilterBuilder().addEqual('output_uuid', collection.uuid).getFilters(),
188 select: containerRequestFieldsNoMounts,
190 const parentLogPromise = services.containerRequestService.list({
191 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
192 filters: new FilterBuilder().addEqual('log_uuid', collection.uuid).getFilters(),
193 select: containerRequestFieldsNoMounts,
195 const [parentOutput, parentLog] = await Promise.all([parentOutputPromise, parentLogPromise]);
196 return parentOutput.items.length > 0 ?
197 parentOutput.items[0] :
198 parentLog.items.length > 0 ?
204 export const setProjectBreadcrumbs = (uuid: string) =>
205 async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
206 const ancestors = await services.ancestorsService.ancestors(uuid, '');
207 const rootUuid = getUserUuid(getState());
208 if (uuid === rootUuid || ancestors.find(ancestor => ancestor.uuid === rootUuid)) {
209 dispatch(setSidePanelBreadcrumbs(uuid));
211 dispatch(setSharedWithMeBreadcrumbs(uuid));
212 dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
216 export const setProcessBreadcrumbs = (processUuid: string) =>
217 (dispatch: Dispatch, getState: () => RootState) => {
218 const { resources } = getState();
219 const process = getProcess(processUuid)(resources);
221 dispatch<any>(setProjectBreadcrumbs(process.containerRequest.ownerUuid));
225 export const setGroupsBreadcrumbs = () =>
227 label: SidePanelTreeCategory.GROUPS,
228 uuid: SidePanelTreeCategory.GROUPS,
229 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
232 export const setGroupDetailsBreadcrumbs = (groupUuid: string) =>
233 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
235 const group = getResource<GroupResource>(groupUuid)(getState().resources);
237 const breadcrumbs: Breadcrumb[] = [
239 label: SidePanelTreeCategory.GROUPS,
240 uuid: SidePanelTreeCategory.GROUPS,
241 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
243 { label: group ? group.name : (await services.groupsService.get(groupUuid)).name, uuid: groupUuid },
246 dispatch(setBreadcrumbs(breadcrumbs));
250 export const USERS_PANEL_LABEL = 'Users';
252 export const setUsersBreadcrumbs = () =>
253 setBreadcrumbs([{ label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL }]);
255 export const setUserProfileBreadcrumbs = (userUuid: string) =>
256 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
258 const user = getResource<UserResource>(userUuid)(getState().resources)
259 || await services.userService.get(userUuid, false);
260 const breadcrumbs: Breadcrumb[] = [
261 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
262 { label: user ? user.username : userUuid, uuid: userUuid },
264 dispatch(setBreadcrumbs(breadcrumbs));
266 const breadcrumbs: Breadcrumb[] = [
267 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
268 { label: userUuid, uuid: userUuid },
270 dispatch(setBreadcrumbs(breadcrumbs));
274 export const MY_ACCOUNT_PANEL_LABEL = 'My Account';
276 export const setMyAccountBreadcrumbs = () =>
277 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
278 dispatch(setBreadcrumbs([
279 { label: MY_ACCOUNT_PANEL_LABEL, uuid: MY_ACCOUNT_PANEL_LABEL },