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 { AdminMenuIcon, CollectionIcon, IconType, ProcessIcon, ProjectIcon, ResourceIcon, 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';
27 import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
29 export const BREADCRUMBS = 'breadcrumbs';
31 export const setBreadcrumbs = (breadcrumbs: any, currentItem?: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource) => {
33 breadcrumbs.push(resourceToBreadcrumb(currentItem));
35 return propertiesActions.SET_PROPERTY({ key: BREADCRUMBS, value: breadcrumbs });
38 const resourceToBreadcrumbIcon = (resource: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource): IconType | undefined => {
39 switch (resource.kind) {
40 case ResourceKind.PROJECT:
42 case ResourceKind.PROCESS:
44 case ResourceKind.COLLECTION:
45 return CollectionIcon;
46 case ResourceKind.WORKFLOW:
53 const resourceToBreadcrumb = (resource: CollectionResource | ContainerRequestResource | GroupResource | WorkflowResource): Breadcrumb => ({
56 icon: resourceToBreadcrumbIcon(resource),
59 export const setSidePanelBreadcrumbs = (uuid: string) =>
60 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
62 dispatch(progressIndicatorActions.START_WORKING(uuid + "-breadcrumbs"));
63 const ancestors = await services.ancestorsService.ancestors(uuid, '');
64 dispatch(updateResources(ancestors));
66 let breadcrumbs: Breadcrumb[] = [];
67 const { collectionPanel: { item } } = getState();
69 const path = getState().router.location!.pathname;
70 const currentUuid = path.split('/')[2];
71 const uuidKind = extractUuidKind(currentUuid);
72 const rootUuid = getUserUuid(getState());
74 if (ancestors.find(ancestor => ancestor.uuid === rootUuid)) {
75 // Handle home project uuid root
77 label: SidePanelTreeCategory.PROJECTS,
78 uuid: SidePanelTreeCategory.PROJECTS,
79 icon: getSidePanelIcon(SidePanelTreeCategory.PROJECTS)
81 } else if (Object.values(SidePanelTreeCategory).includes(uuid as SidePanelTreeCategory)) {
82 // Handle SidePanelTreeCategory root
86 icon: getSidePanelIcon(uuid)
90 breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) =>
91 ancestor.kind === ResourceKind.GROUP
92 ? [...breadcrumbs, resourceToBreadcrumb(ancestor)]
96 if (uuidKind === ResourceKind.COLLECTION) {
97 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
98 const parentProcessItem = await getCollectionParent(collectionItem)(services);
99 if (parentProcessItem) {
100 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
101 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
102 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
104 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
105 } else if (uuidKind === ResourceKind.PROCESS) {
106 const processItem = await services.containerRequestService.get(currentUuid);
107 const parentProcessItem = await getProcessParent(processItem)(services);
108 if (parentProcessItem) {
109 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
111 dispatch(setBreadcrumbs(breadcrumbs, processItem));
112 } else if (uuidKind === ResourceKind.WORKFLOW) {
113 const workflowItem = await services.workflowService.get(currentUuid);
114 dispatch(setBreadcrumbs(breadcrumbs, workflowItem));
116 dispatch(setBreadcrumbs(breadcrumbs));
118 dispatch(progressIndicatorActions.STOP_WORKING(uuid + "-breadcrumbs"));
122 export const setSharedWithMeBreadcrumbs = (uuid: string) =>
123 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.SHARED_WITH_ME);
125 export const setTrashBreadcrumbs = (uuid: string) =>
126 setCategoryBreadcrumbs(uuid, SidePanelTreeCategory.TRASH);
128 export const setCategoryBreadcrumbs = (uuid: string, category: string) =>
129 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
131 dispatch(progressIndicatorActions.START_WORKING(uuid + "-breadcrumbs"));
132 const ancestors = await services.ancestorsService.ancestors(uuid, '');
133 dispatch(updateResources(ancestors));
134 const initialBreadcrumbs: Breadcrumb[] = [
138 icon: getSidePanelIcon(category)
141 const { collectionPanel: { item } } = getState();
142 const path = getState().router.location!.pathname;
143 const currentUuid = path.split('/')[2];
144 const uuidKind = extractUuidKind(currentUuid);
145 let breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) =>
146 ancestor.kind === ResourceKind.GROUP
147 ? [...breadcrumbs, resourceToBreadcrumb(ancestor)]
150 if (uuidKind === ResourceKind.COLLECTION) {
151 const collectionItem = item ? item : await services.collectionService.get(currentUuid);
152 const parentProcessItem = await getCollectionParent(collectionItem)(services);
153 if (parentProcessItem) {
154 const mainProcessItem = await getProcessParent(parentProcessItem)(services);
155 mainProcessItem && breadcrumbs.push(resourceToBreadcrumb(mainProcessItem));
156 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
158 dispatch(setBreadcrumbs(breadcrumbs, collectionItem));
159 } else if (uuidKind === ResourceKind.PROCESS) {
160 const processItem = await services.containerRequestService.get(currentUuid);
161 const parentProcessItem = await getProcessParent(processItem)(services);
162 if (parentProcessItem) {
163 breadcrumbs.push(resourceToBreadcrumb(parentProcessItem));
165 dispatch(setBreadcrumbs(breadcrumbs, processItem));
166 } else if (uuidKind === ResourceKind.WORKFLOW) {
167 const workflowItem = await services.workflowService.get(currentUuid);
168 dispatch(setBreadcrumbs(breadcrumbs, workflowItem));
170 dispatch(setBreadcrumbs(breadcrumbs));
172 dispatch(progressIndicatorActions.STOP_WORKING(uuid + "-breadcrumbs"));
176 const getProcessParent = (childProcess: ContainerRequestResource) =>
177 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
178 if (childProcess.requestingContainerUuid) {
179 const parentProcesses = await services.containerRequestService.list({
180 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
181 filters: new FilterBuilder().addEqual('container_uuid', childProcess.requestingContainerUuid).getFilters(),
182 select: containerRequestFieldsNoMounts,
184 if (parentProcesses.items.length > 0) {
185 return parentProcesses.items[0];
194 const getCollectionParent = (collection: CollectionResource) =>
195 async (services: ServiceRepository): Promise<ContainerRequestResource | undefined> => {
196 const parentOutputPromise = services.containerRequestService.list({
197 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
198 filters: new FilterBuilder().addEqual('output_uuid', collection.uuid).getFilters(),
199 select: containerRequestFieldsNoMounts,
201 const parentLogPromise = services.containerRequestService.list({
202 order: new OrderBuilder<ProcessResource>().addAsc('createdAt').getOrder(),
203 filters: new FilterBuilder().addEqual('log_uuid', collection.uuid).getFilters(),
204 select: containerRequestFieldsNoMounts,
206 const [parentOutput, parentLog] = await Promise.all([parentOutputPromise, parentLogPromise]);
207 return parentOutput.items.length > 0 ?
208 parentOutput.items[0] :
209 parentLog.items.length > 0 ?
215 export const setProjectBreadcrumbs = (uuid: string) =>
216 async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
217 const ancestors = await services.ancestorsService.ancestors(uuid, '');
218 const rootUuid = getUserUuid(getState());
219 if (uuid === rootUuid || ancestors.find(ancestor => ancestor.uuid === rootUuid)) {
220 dispatch(setSidePanelBreadcrumbs(uuid));
222 dispatch(setSharedWithMeBreadcrumbs(uuid));
223 dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
227 export const setProcessBreadcrumbs = (processUuid: string) =>
228 (dispatch: Dispatch, getState: () => RootState) => {
229 const { resources } = getState();
230 const process = getProcess(processUuid)(resources);
232 dispatch<any>(setProjectBreadcrumbs(process.containerRequest.ownerUuid));
236 export const setGroupsBreadcrumbs = () =>
238 label: SidePanelTreeCategory.GROUPS,
239 uuid: SidePanelTreeCategory.GROUPS,
240 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
243 export const setGroupDetailsBreadcrumbs = (groupUuid: string) =>
244 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
246 const group = getResource<GroupResource>(groupUuid)(getState().resources);
248 const breadcrumbs: Breadcrumb[] = [
250 label: SidePanelTreeCategory.GROUPS,
251 uuid: SidePanelTreeCategory.GROUPS,
252 icon: getSidePanelIcon(SidePanelTreeCategory.GROUPS)
254 { label: group ? group.name : (await services.groupsService.get(groupUuid)).name, uuid: groupUuid },
257 dispatch(setBreadcrumbs(breadcrumbs));
261 export const USERS_PANEL_LABEL = 'Users';
263 export const setUsersBreadcrumbs = () =>
264 setBreadcrumbs([{ label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL }]);
266 export const setUserProfileBreadcrumbs = (userUuid: string) =>
267 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
269 const user = getResource<UserResource>(userUuid)(getState().resources)
270 || await services.userService.get(userUuid, false);
271 const breadcrumbs: Breadcrumb[] = [
272 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
273 { label: user ? user.username : userUuid, uuid: userUuid },
275 dispatch(setBreadcrumbs(breadcrumbs));
277 const breadcrumbs: Breadcrumb[] = [
278 { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL },
279 { label: userUuid, uuid: userUuid },
281 dispatch(setBreadcrumbs(breadcrumbs));
285 export const MY_ACCOUNT_PANEL_LABEL = 'My Account';
287 export const setMyAccountBreadcrumbs = () =>
288 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
289 dispatch(setBreadcrumbs([
290 { label: MY_ACCOUNT_PANEL_LABEL, uuid: MY_ACCOUNT_PANEL_LABEL },
294 export const INSTANCE_TYPES_PANEL_LABEL = 'Instance Types';
296 export const setInstanceTypesBreadcrumbs = () =>
297 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
298 dispatch(setBreadcrumbs([
299 { label: INSTANCE_TYPES_PANEL_LABEL, uuid: INSTANCE_TYPES_PANEL_LABEL, icon: ResourceIcon },
303 export const VIRTUAL_MACHINES_USER_PANEL_LABEL = 'Virtual Machines';
305 export const setVirtualMachinesBreadcrumbs = () =>
306 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
307 dispatch(setBreadcrumbs([
308 { label: VIRTUAL_MACHINES_USER_PANEL_LABEL, uuid: VIRTUAL_MACHINES_USER_PANEL_LABEL },
312 export const VIRTUAL_MACHINES_ADMIN_PANEL_LABEL = 'Virtual Machines Admin';
314 export const setVirtualMachinesAdminBreadcrumbs = () =>
315 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
316 dispatch(setBreadcrumbs([
317 { label: VIRTUAL_MACHINES_ADMIN_PANEL_LABEL, uuid: VIRTUAL_MACHINES_ADMIN_PANEL_LABEL, icon: AdminMenuIcon },
321 export const REPOSITORIES_PANEL_LABEL = 'Repositories';
323 export const setRepositoriesBreadcrumbs = () =>
324 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
325 dispatch(setBreadcrumbs([
326 { label: REPOSITORIES_PANEL_LABEL, uuid: REPOSITORIES_PANEL_LABEL, icon: AdminMenuIcon },