]> git.arvados.org - arvados.git/blob - services/workbench2/src/common/link-update-name.ts
Merge branch '22970-go124'
[arvados.git] / services / workbench2 / src / common / link-update-name.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
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';
12
13 type NameableResource = Resource & { name?: string };
14
15 /**
16  * Validates links are not to trashed resources and updates link resource names
17  * to match resource name if necessary
18  */
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
23     if (!headResource) {
24         try {
25             headResource = await fetchResource(link.headUuid)(dispatch, getState, services);
26         } catch (e) {
27             // If not found, assume deleted permanently and suppress this entry
28             if (getCommonResourceServiceError(e) === CommonResourceServiceError.NOT_FOUND) {
29                 return undefined;
30             }
31             // If non-404 exception was raised, fall through to the headResource check
32         }
33         // Any other error we keep the entry but skip updating the name
34         if (!headResource) {
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
37             if (!link.name) {
38                 return undefined
39             };
40             return link
41         }
42     }
43     // If resource is trashed, filter it out
44     if ((headResource as TrashableResource).isTrashed) {
45         return undefined;
46     }
47
48     if (validateLinkNameProp(link, headResource) === true) return link;
49
50     const updatedLink = updateLinkNameProp(link, headResource);
51     updateRemoteLinkName(updatedLink)(dispatch, getState, services);
52
53     return updatedLink;
54 };
55
56 /**
57  * Filters links to trashed / 404ed resources and updates link name to match resource
58  */
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));
64
65     return Promise.resolve(validLinks);
66 };
67
68 /**
69  * Fetches any resource type for verifying link names / trash status
70  * Exposes exceptions to allow the consumer to differentiate errors
71  */
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);
75     if (service) {
76         return await service.get(uuid, showErrors);
77     }
78     return undefined;
79 };
80
81 const validateLinkNameProp = (link: LinkResource, head: NameableResource) => {
82     if (!link.name || link.name !== head.name) return false;
83     return true;
84 };
85
86 const updateLinkNameProp = (link: LinkResource, head: NameableResource): LinkResource => {
87     const updatedLink = { ...link };
88     if (head.name) updatedLink.name = head.name;
89     return updatedLink;
90 };
91
92 const updateRemoteLinkName = (link: LinkResource) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
93     try {
94         const kind = extractUuidKind(link.uuid);
95         const service = getResourceService(kind)(services);
96         if (service) {
97             service.update(link.uuid, { name: link.name });
98         }
99     } catch (error) {
100         console.error('Could not update link name', link, error);
101     }
102 };