Merge remote-tracking branch 'origin/main' into 18207-Workbench2-is-not-clearing...
[arvados-workbench2.git] / src / store / resources / resources-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { unionize, ofType, UnionOf } from 'common/unionize';
6 import { extractUuidKind, Resource, ResourceWithProperties } from 'models/resource';
7 import { Dispatch } from 'redux';
8 import { RootState } from 'store/store';
9 import { ServiceRepository } from 'services/services';
10 import { getResourceService } from 'services/services';
11 import { addProperty, deleteProperty } from 'lib/resource-properties';
12 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
13 import { getResource } from './resources';
14 import { TagProperty } from 'models/tag';
15 import { change, formValueSelector } from 'redux-form';
16 import { ResourcePropertiesFormData } from 'views-components/resource-properties-form/resource-properties-form';
17
18 export const resourcesActions = unionize({
19     SET_RESOURCES: ofType<Resource[]>(),
20     DELETE_RESOURCES: ofType<string[]>()
21 });
22
23 export type ResourcesAction = UnionOf<typeof resourcesActions>;
24
25 export const updateResources = (resources: Resource[]) => resourcesActions.SET_RESOURCES(resources);
26
27 export const deleteResources = (resources: string[]) => resourcesActions.DELETE_RESOURCES(resources);
28
29 export const loadResource = (uuid: string, showErrors?: boolean) =>
30     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
31         try {
32             const kind = extractUuidKind(uuid);
33             const service = getResourceService(kind)(services);
34             if (service) {
35                 const resource = await service.get(uuid, showErrors);
36                 dispatch<any>(updateResources([resource]));
37                 return resource;
38             }
39         } catch {}
40         return undefined;
41     };
42
43 export const deleteResourceProperty = (uuid: string, key: string, value: string) =>
44     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
45         const { resources } = getState();
46
47         const rsc = getResource(uuid)(resources) as ResourceWithProperties;
48         if (!rsc) { return; }
49
50         const kind = extractUuidKind(uuid);
51         const service = getResourceService(kind)(services);
52         if (!service) { return; }
53
54         const properties = Object.assign({}, rsc.properties);
55
56         try {
57             let updatedRsc = await service.update(
58                 uuid, {
59                     properties: deleteProperty(properties, key, value),
60                 });
61             updatedRsc = {...rsc, ...updatedRsc};
62             dispatch<any>(updateResources([updatedRsc]));
63             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
64         } catch (e) {
65             dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR }));
66         }
67     };
68
69 export const createResourceProperty = (data: TagProperty) =>
70     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
71         const { uuid } = data;
72         const { resources } = getState();
73
74         const rsc = getResource(uuid)(resources) as ResourceWithProperties;
75         if (!rsc) { return; }
76
77         const kind = extractUuidKind(uuid);
78         const service = getResourceService(kind)(services);
79         if (!service) { return; }
80
81         try {
82             const key = data.keyID || data.key;
83             const value = data.valueID || data.value;
84             const properties = Object.assign({}, rsc.properties);
85             let updatedRsc = await service.update(
86                 rsc.uuid, {
87                     properties: addProperty(properties, key, value),
88                 }
89             );
90             updatedRsc = {...rsc, ...updatedRsc};
91             dispatch<any>(updateResources([updatedRsc]));
92             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully added.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
93         } catch (e) {
94             const errorMsg = e.errors && e.errors.length > 0 ? e.errors[0] : "Error while adding property";
95             dispatch(snackbarActions.OPEN_SNACKBAR({ message: errorMsg, hideDuration: 2000, kind: SnackbarKind.ERROR }));
96         }
97     };
98
99 export const addPropertyToResourceForm = (data: ResourcePropertiesFormData, formName: string) =>
100     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
101         const properties = { ...formValueSelector(formName)(getState(), 'properties') };
102         const key = data.keyID || data.key;
103         const value =  data.valueID || data.value;
104         dispatch(change(
105             formName,
106             'properties',
107             addProperty(properties, key, value)));
108     };
109
110 export const removePropertyFromResourceForm = (key: string, value: string, formName: string) =>
111     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
112         const properties = { ...formValueSelector(formName)(getState(), 'properties') };
113         dispatch(change(
114             formName,
115             'properties',
116             deleteProperty(properties, key, value)));
117     };