Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / services / common-service / common-resource-service.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { AxiosInstance } from "axios";
6 import { snakeCase } from "lodash";
7 import { Resource } from "models/resource";
8 import { ApiActions } from "services/api/api-actions";
9 import { CommonService } from "services/common-service/common-service";
10
11 export enum CommonResourceServiceError {
12     UNIQUE_NAME_VIOLATION = 'UniqueNameViolation',
13     OWNERSHIP_CYCLE = 'OwnershipCycle',
14     MODIFYING_CONTAINER_REQUEST_FINAL_STATE = 'ModifyingContainerRequestFinalState',
15     NAME_HAS_ALREADY_BEEN_TAKEN = 'NameHasAlreadyBeenTaken',
16     PERMISSION_ERROR_FORBIDDEN = 'PermissionErrorForbidden',
17     SOURCE_DESTINATION_CANNOT_BE_SAME = 'SourceDestinationCannotBeSame',
18     UNKNOWN = 'Unknown',
19     NONE = 'None'
20 }
21
22 export class CommonResourceService<T extends Resource> extends CommonService<T> {
23     constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions, readOnlyFields: string[] = []) {
24         super(serverApi, resourceType, actions, readOnlyFields.concat([
25             'uuid',
26             'etag',
27             'kind',
28             'canWrite',
29             'canManage',
30             'createdAt',
31             'modifiedAt',
32             'modifiedByClientUuid',
33             'modifiedByUserUuid'
34         ]));
35     }
36
37     create(data?: Partial<T>, showErrors?: boolean) {
38         let payload: any;
39         if (data !== undefined) {
40             this.readOnlyFields.forEach(field => delete data[field]);
41             payload = {
42                 [this.resourceType.slice(0, -1)]: CommonService.mapKeys(snakeCase)(data),
43             };
44         }
45         return super.create(payload, showErrors);
46     }
47
48     update(uuid: string, data: Partial<T>, showErrors?: boolean, select?: string[]) {
49         let payload: any;
50         if (data !== undefined) {
51             this.readOnlyFields.forEach(field => delete data[field]);
52             payload = {
53                 [this.resourceType.slice(0, -1)]: CommonService.mapKeys(snakeCase)(data),
54             };
55             if (select !== undefined && select.length > 0) {
56                 payload.select = ['uuid', ...select.map(field => snakeCase(field))];
57             };
58         }
59         return super.update(uuid, payload, showErrors);
60     }
61 }
62
63 export const getCommonResourceServiceError = (errorResponse: any) => {
64     if (errorResponse && 'errors' in errorResponse) {
65         const error = errorResponse.errors.join('');
66         switch (true) {
67             case /UniqueViolation/.test(error):
68                 return CommonResourceServiceError.UNIQUE_NAME_VIOLATION;
69             case /ownership cycle/.test(error):
70                 return CommonResourceServiceError.OWNERSHIP_CYCLE;
71             case /Mounts cannot be modified in state 'Final'/.test(error):
72                 return CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE;
73             case /Name has already been taken/.test(error):
74                 return CommonResourceServiceError.NAME_HAS_ALREADY_BEEN_TAKEN;
75             case /403 Forbidden/.test(error):
76                 return CommonResourceServiceError.PERMISSION_ERROR_FORBIDDEN;
77             case new RegExp(CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME).test(error):
78                 return CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME;
79             default:
80                 return CommonResourceServiceError.UNKNOWN;
81         }
82     }
83     return CommonResourceServiceError.NONE;
84 };