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 { TreePicker } from '../tree-picker/tree-picker';
10 import { getSidePanelTreeBranch, getSidePanelTreeNodeAncestorsIds } from '../side-panel-tree/side-panel-tree-actions';
11 import { propertiesActions } from '../properties/properties-actions';
12 import { getProcess } from 'store/processes/process';
13 import { ServiceRepository } from 'services/services';
14 import { SidePanelTreeCategory, activateSidePanelTreeItem } from 'store/side-panel-tree/side-panel-tree-actions';
15 import { updateResources } from '../resources/resources-actions';
16 import { ResourceKind } from 'models/resource';
17 import { GroupResource } from 'models/group';
18 import { extractUuidKind } from 'models/resource';
19 import { UserResource } from 'models/user';
20 import { FilterBuilder } from 'services/api/filter-builder';
21 import { ProcessResource } from 'models/process';
22 import { OrderBuilder } from 'services/api/order-builder';
23 import { Breadcrumb } from 'components/breadcrumbs/breadcrumbs';
24 import { ContainerRequestResource, containerRequestFieldsNoMounts } from 'models/container-request';
25 import { CollectionIcon, IconType, ProcessIcon, ProjectIcon } from 'components/icon/icon';
26 import { CollectionResource } from 'models/collection';
27 import { getSidePanelIcon } from 'views-components/side-panel-tree/side-panel-tree';
29 export const BREADCRUMBS = 'breadcrumbs';
31 export const setBreadcrumbs = (breadcrumbs: any, currentItem?: CollectionResource | ContainerRequestResource | GroupResource) => {
33 breadcrumbs.push(resourceToBreadcrumb(currentItem));
35 return propertiesActions.SET_PROPERTY({ key: BREADCRUMBS, value: breadcrumbs });
38 const resourceToBreadcrumbIcon = (resource: CollectionResource | ContainerRequestResource | GroupResource): IconType | undefined => {
39 switch (resource.kind) {
40 case ResourceKind.PROJECT:
42 case ResourceKind.PROCESS:
44 case ResourceKind.COLLECTION:
45 return CollectionIcon;
51 const resourceToBreadcrumb = (resource: CollectionResource | ContainerRequestResource | GroupResource): Breadcrumb => ({
54 icon: resourceToBreadcrumbIcon(resource),
57 const getSidePanelTreeBreadcrumbs = (uuid: string) => (treePicker: TreePicker): Breadcrumb[] => {
58 const nodes = getSidePanelTreeBranch(uuid)(treePicker);
59 return nodes.map(node =>
60 typeof node.value === 'string'
64 icon: getSidePanelIcon(node.value)
66 : resourceToBreadcrumb(node.value));
69 export const setSidePanelBreadcrumbs = (uuid: string) =>
70 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
71 const { treePicker, collectionPanel: { item } } = getState();
72 const breadcrumbs = getSidePanelTreeBreadcrumbs(uuid)(treePicker);
73 const path = getState().router.location!.pathname;
74 const currentUuid = path.split('/')[2];
75 const uuidKind = extractUuidKind(currentUuid);
77 if (uuidKind === ResourceKind.COLLECTION) {
78 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
79 const parentProcessItem = await getCollectionParent(collectionItem)(services);
80 if (parentProcessItem) {
81 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
82 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
83 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
85 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
86 } else if (uuidKind === ResourceKind.PROCESS) {
87 const processItem = await services.containerRequestService.get(currentUuid);
88 const parentProcessItem = await getProcessParent(processItem)(services);
89 if (parentProcessItem) {
90 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
92 dispatch(setBreadcrumbs(breadcrumbs, processItem));
94 dispatch(setBreadcrumbs(breadcrumbs));
97 export const setSharedWithMeBreadcrumbs = (uuid: string) =>
98 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.SHARED_WITH_ME);
100 export const setTrashBreadcrumbs = (uuid: string) =>
101 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.TRASH);
103 export const setCategoryBreadcrumbs = (uuid: string, category: string) =>
104 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
105 const ancestors = await services.ancestorsService.ancestors(uuid, '');
106 dispatch(updateResources(ancestors));
107 const initialBreadcrumbs: Breadcrumb[] = [
111 icon: getSidePanelIcon(category)
114 const { collectionPanel: { item } } = getState();
115 const path = getState().router.location!.pathname;
116 const currentUuid = path.split('/')[2];
117 const uuidKind = extractUuidKind(currentUuid);
118 let breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) =>
119 ancestor.kind === ResourceKind.GROUP
120 ? [...breadcrumbs, resourceToBreadcrumb(ancestor)]
123 if (uuidKind === ResourceKind.COLLECTION) {
124 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
125 const parentProcessItem = await getCollectionParent(collectionItem)(services);
126 if (parentProcessItem) {
127 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
128 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
129 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
131 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
132 } else if (uuidKind === ResourceKind.PROCESS) {
133 const processItem = await services.containerRequestService.get(currentUuid);
134 const parentProcessItem = await getProcessParent(processItem)(services);
135 if (parentProcessItem) {
136 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
138 dispatch(setBreadcrumbs(breadcrumbs, processItem));
140 dispatch(setBreadcrumbs(breadcrumbs));
143 const getProcessParent = (childProcess: ContainerRequestResource) =>
144 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
145 if (childProcess.requestingContainerUuid) {
146 const parentProcesses = await services.containerRequestService.list({
147 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
148 filters: new FilterBuilder().addEqual('container_uuid', childProcess.requestingContainerUuid).getFilters(),
149 select: containerRequestFieldsNoMounts,
151 if (parentProcesses.items.length > 0) {
152 return parentProcesses.items[0];
161 const getCollectionParent = (collection: CollectionResource) =>
162 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
163 const parentOutputPromise = services.containerRequestService.list({
164 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
165 filters: new FilterBuilder().addEqual('output_uuid', collection.uuid).getFilters(),
166 select: containerRequestFieldsNoMounts,
168 const parentLogPromise = services.containerRequestService.list({
169 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
170 filters: new FilterBuilder().addEqual('log_uuid', collection.uuid).getFilters(),
171 select: containerRequestFieldsNoMounts,
173 const [parentOutput, parentLog] = await Promise.all([parentOutputPromise, parentLogPromise]);
174 return parentOutput.items.length > 0 ?
175 parentOutput.items[0] :
176 parentLog.items.length > 0 ?
182 export const setProjectBreadcrumbs = (uuid: string) =>
183 (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
184 const ancestors = getSidePanelTreeNodeAncestorsIds(uuid)(getState().treePicker);
185 const rootUuid = getUserUuid(getState());
186 if (uuid === rootUuid || ancestors.find(uuid => uuid === rootUuid)) {
187 dispatch(setSidePanelBreadcrumbs(uuid));
189 dispatch(setSharedWithMeBreadcrumbs(uuid));
190 dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
194 export const setProcessBreadcrumbs = (processUuid: string) =>
195 (dispatch: Dispatch, getState: () => RootState) => {
196 const { resources } = getState();
197 const process = getProcess(processUuid)(resources);
199 dispatch<any>(setProjectBreadcrumbs(process.containerRequest.ownerUuid));
203 export const setGroupsBreadcrumbs = () =>
205 label: SidePanelTreeCategory.GROUPS,
206 uuid: SidePanelTreeCategory.GROUPS,
207 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
210 export const setGroupDetailsBreadcrumbs = (groupUuid: string) =>
211 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
213 const group = getResource<GroupResource>(groupUuid)(getState().resources);
215 const breadcrumbs: Breadcrumb[] = [
217 label: SidePanelTreeCategory.GROUPS,
218 uuid: SidePanelTreeCategory.GROUPS,
219 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
221 { label: group ? group.name : (await services.groupsService.get(groupUuid)).name, uuid: groupUuid },
224 dispatch(setBreadcrumbs(breadcrumbs));
228 export const USERS_PANEL_LABEL = 'Users';
230 export const setUsersBreadcrumbs = () =>
231 setBreadcrumbs([{ label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL }]);
233 export const setUserProfileBreadcrumbs = (userUuid: string) =>
234 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
236 const user = getResource<UserResource>(userUuid)(getState().resources)
237 || await services.userService.get(userUuid, false);
238 const breadcrumbs: Breadcrumb[] = [
239 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
240 { label: user ? user.username : userUuid, uuid: userUuid },
242 dispatch(setBreadcrumbs(breadcrumbs));
244 const breadcrumbs: Breadcrumb[] = [
245 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
246 { label: userUuid, uuid: userUuid },
248 dispatch(setBreadcrumbs(breadcrumbs));
252 export const MY_ACCOUNT_PANEL_LABEL = 'My Account';
254 export const setMyAccountBreadcrumbs = () =>
255 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
256 dispatch(setBreadcrumbs([
257 { label: MY_ACCOUNT_PANEL_LABEL, uuid: MY_ACCOUNT_PANEL_LABEL },