--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { LinkResource } from 'models/link';
+import { Dispatch } from 'redux';
+import { RootState } from 'store/store';
+import { ServiceRepository, getResourceService } from 'services/services';
+import { Resource, extractUuidKind } from 'models/resource';
+
+type NameableResource = Resource & { name?: string };
+
+export const verifyAndUpdateLink = async (link: LinkResource, dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<LinkResource> => {
+ //head resource should already be in the store
+ let headResource: Resource | undefined = getState().resources[link.headUuid];
+ //if not, fetch it
+ if (!headResource) {
+ headResource = await fetchResource(link.headUuid)(dispatch, getState, services);
+ if (!headResource) {
+ if (!link.name) console.error('Could not validate link', link, 'because link head', link.headUuid, 'is not available');
+ return link;
+ }
+ }
+
+ if (validateLinkNameProp(link, headResource) === true) return link;
+
+ const updatedLink = updateLinkNameProp(link, headResource);
+ updateRemoteLinkName(updatedLink)(dispatch, getState, services);
+
+ return updatedLink;
+};
+
+export const verifyAndUpdateLinks = async (links: LinkResource[], dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+
+ const updatedLinks = links.map((link) => verifyAndUpdateLink(link, dispatch, getState, services));
+ return Promise.all(updatedLinks);
+};
+
+const fetchResource = (uuid: string, showErrors?: boolean) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ try {
+ const kind = extractUuidKind(uuid);
+ const service = getResourceService(kind)(services);
+ if (service) {
+ const resource = await service.get(uuid, showErrors);
+ return resource;
+ }
+ } catch (e) {
+ console.error(`Could not fetch resource ${uuid}`, e);
+ }
+ return undefined;
+};
+
+const validateLinkNameProp = (link: LinkResource, head: NameableResource) => {
+ if (!link.name || link.name !== head.name) return false;
+ return true;
+};
+
+const updateLinkNameProp = (link: LinkResource, head: NameableResource) => {
+ const updatedLink = { ...link };
+ if (head.name) updatedLink.name = head.name;
+ return updatedLink;
+};
+
+const updateRemoteLinkName = (link: LinkResource) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ try {
+ const kind = extractUuidKind(link.uuid);
+ const service = getResourceService(kind)(services);
+ if (service) {
+ service.update(link.uuid, { name: link.name });
+ }
+ } catch (error) {
+ console.error('Could not update link name', link, error);
+ }
+};
import { ResourceKind } from 'models/resource';
import { CategoriesListReducer } from 'common/plugintypes';
import { pluginConfig } from 'plugins';
-import { LinkClass } from 'models/link';
+import { LinkClass, LinkResource } from 'models/link';
+import { verifyAndUpdateLinks } from 'common/link-update-name';
export enum SidePanelTreeCategory {
PROJECTS = 'Home Projects',
const treePicker = getTreePicker(SIDE_PANEL_TREE)(getState().treePicker);
const node = treePicker ? getNode(projectUuid)(treePicker) : undefined;
if (projectUuid === SidePanelTreeCategory.PUBLIC_FAVORITES) {
- await dispatch<any>(loadPublicFavoritesTree());
+ const unverifiedPubFaves = await dispatch<any>(loadPublicFavoritesTree());
+ verifyAndUpdateLinkNames(unverifiedPubFaves, dispatch, getState, services);
} else if (projectUuid === SidePanelTreeCategory.FAVORITES) {
- await dispatch<any>(loadFavoritesTree());
+ const unverifiedFaves = await dispatch<any>(loadFavoritesTree());
+ await setFaves(unverifiedFaves, dispatch, getState, services);
+ verifyAndUpdateLinkNames(unverifiedFaves, dispatch, getState, services);
} else if (node || projectUuid !== '') {
await dispatch<any>(loadProject(projectUuid));
}
})
);
- dispatch(resourcesActions.SET_RESOURCES(items));
+ return items;
+};
+
+const setFaves = async(links: LinkResource[], dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+
+ const uuids = links.map(it => it.headUuid);
+ const groupItems: Promise<any> = services.groupsService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .getFilters()
+ });
+ const collectionItems: Promise<any> = services.collectionService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .getFilters()
+ });
+ const processItems: Promise<any> = services.containerRequestService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .getFilters()
+ });
+
+ const resolvedItems = await Promise.all([groupItems, collectionItems, processItems]);
+
+ const responseItems = resolvedItems.reduce((acc, response) => acc.concat(response.items), []);
+
+ //setting resources here so they won't be re-fetched in validation step
+ dispatch(resourcesActions.SET_RESOURCES(responseItems));
+};
+
+const verifyAndUpdateLinkNames = async (links: LinkResource[], dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const verfifiedLinks = await verifyAndUpdateLinks(links, dispatch, getState, services);
+
+ dispatch(
+ treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
+ id: SidePanelTreeCategory.FAVORITES,
+ pickerId: SIDE_PANEL_TREE,
+ nodes: verfifiedLinks.map(item => initTreeNode({ id: item.headUuid, value: item })),
+ })
+ );
};
export const loadPublicFavoritesTree = () => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const { items } = await services.linkService.list(params);
+ const uuids = items.map(it => it.headUuid);
+ const groupItems: Promise<any> = services.groupsService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .addIsA("uuid", typeFilters)
+ .getFilters()
+ });
+ const collectionItems: Promise<any> = services.collectionService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .addIsA("uuid", typeFilters)
+ .getFilters()
+ });
+ const processItems: Promise<any> = services.containerRequestService.list({
+ filters: new FilterBuilder()
+ .addIn("uuid", uuids)
+ .addIsA("uuid", typeFilters)
+ .getFilters()
+ });
+
+ const resolvedItems = await Promise.all([groupItems, collectionItems, processItems]);
+
+ const responseItems = resolvedItems.reduce((acc, response) => acc.concat(response.items), []);
+
+ const filteredItems = items.filter(item => responseItems.some(responseItem => responseItem.uuid === item.headUuid));
+
dispatch(
treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
id: SidePanelTreeCategory.PUBLIC_FAVORITES,
pickerId: SIDE_PANEL_TREE,
- nodes: items.map(item => initTreeNode({ id: item.headUuid, value: item })),
+ nodes: filteredItems.map(item => initTreeNode({ id: item.headUuid, value: item })),
})
);
- dispatch(resourcesActions.SET_RESOURCES(items));
+ //setting resources here so they won't be re-fetched in validation step
+ dispatch(resourcesActions.SET_RESOURCES(responseItems));
+
+ return filteredItems;
};
export const activateSidePanelTreeItem = (id: string) =>