1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { LinkResource } from 'models/link';
6 import { Dispatch } from 'redux';
7 import { RootState } from 'store/store';
8 import { ServiceRepository, getResourceService } from 'services/services';
9 import { Resource, TrashableResource, extractUuidKind } from 'models/resource';
10 import { CommonResourceServiceError, getCommonResourceServiceError } from 'services/common-service/common-resource-service';
11 import { getResource } from 'store/resources/resources';
13 type NameableResource = Resource & { name?: string };
16 * Validates links are not to trashed resources and updates link resource names
17 * to match resource name if necessary
19 const verifyAndUpdateLink = async (link: LinkResource, dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<LinkResource | undefined> => {
20 //head resource should already be in the store
21 let headResource = getResource<Resource>(link.headUuid)(getState().resources);
22 //If resource not in store, fetch it
25 headResource = await fetchResource(link.headUuid)(dispatch, getState, services);
27 // If not found, assume deleted permanently and suppress this entry
28 if (getCommonResourceServiceError(e) === CommonResourceServiceError.NOT_FOUND) {
31 // If non-404 exception was raised, fall through to the headResource check
33 // Any other error we keep the entry but skip updating the name
35 console.error('Could not validate link', link, 'because link head', link.headUuid, 'is not available');
36 // if it's missing name, we can't render it, so skip it
43 // If resource is trashed, filter it out
44 if ((headResource as TrashableResource).isTrashed) {
48 if (validateLinkNameProp(link, headResource) === true) return link;
50 const updatedLink = updateLinkNameProp(link, headResource);
51 updateRemoteLinkName(updatedLink)(dispatch, getState, services);
57 * Filters links to trashed / 404ed resources and updates link name to match resource
59 export const verifyAndUpdateLinks = async (links: LinkResource[], dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<LinkResource[]> => {
60 // Verify and update links in paralell
61 const updatedLinks = links.map((link) => verifyAndUpdateLink(link, dispatch, getState, services));
62 // Filter out undefined links (trashed, or 404)
63 const validLinks = (await Promise.all(updatedLinks)).filter((link): link is LinkResource => (link !== undefined));
65 return Promise.resolve(validLinks);
69 * Fetches any resource type for verifying link names / trash status
70 * Exposes exceptions to allow the consumer to differentiate errors
72 const fetchResource = (uuid: string, showErrors?: boolean) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<Resource | undefined> => {
73 const kind = extractUuidKind(uuid);
74 const service = getResourceService(kind)(services);
76 return await service.get(uuid, showErrors);
81 const validateLinkNameProp = (link: LinkResource, head: NameableResource) => {
82 if (!link.name || link.name !== head.name) return false;
86 const updateLinkNameProp = (link: LinkResource, head: NameableResource): LinkResource => {
87 const updatedLink = { ...link };
88 if (head.name) updatedLink.name = head.name;
92 const updateRemoteLinkName = (link: LinkResource) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
94 const kind = extractUuidKind(link.uuid);
95 const service = getResourceService(kind)(services);
97 service.update(link.uuid, { name: link.name });
100 console.error('Could not update link name', link, error);