Add ssh keys panel
[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 * as _ from "lodash";
6 import { AxiosInstance, AxiosPromise } from "axios";
7 import { Resource } from "src/models/resource";
8 import * as uuid from "uuid/v4";
9 import { ApiActions } from "~/services/api/api-actions";
10
11 export interface ListArguments {
12     limit?: number;
13     offset?: number;
14     filters?: string;
15     order?: string;
16     select?: string[];
17     distinct?: boolean;
18     count?: string;
19 }
20
21 export interface ListResults<T> {
22     kind: string;
23     offset: number;
24     limit: number;
25     items: T[];
26     itemsAvailable: number;
27 }
28
29 export interface Errors {
30     errors: string[];
31     errorToken: string;
32 }
33
34 export enum CommonResourceServiceError {
35     UNIQUE_VIOLATION = 'UniqueViolation',
36     OWNERSHIP_CYCLE = 'OwnershipCycle',
37     MODIFYING_CONTAINER_REQUEST_FINAL_STATE = 'ModifyingContainerRequestFinalState',
38     UNIQUE_PUBLIC_KEY = 'UniquePublicKey',
39     INVALID_PUBLIC_KEY = 'InvalidPublicKey',
40     UNKNOWN = 'Unknown',
41     NONE = 'None'
42 }
43
44 export class CommonResourceService<T extends Resource> {
45
46     static mapResponseKeys = (response: { data: any }) =>
47         CommonResourceService.mapKeys(_.camelCase)(response.data)
48
49     static mapKeys = (mapFn: (key: string) => string) =>
50         (value: any): any => {
51             switch (true) {
52                 case _.isPlainObject(value):
53                     return Object
54                         .keys(value)
55                         .map(key => [key, mapFn(key)])
56                         .reduce((newValue, [key, newKey]) => ({
57                             ...newValue,
58                             [newKey]: CommonResourceService.mapKeys(mapFn)(value[key])
59                         }), {});
60                 case _.isArray(value):
61                     return value.map(CommonResourceService.mapKeys(mapFn));
62                 default:
63                     return value;
64             }
65         }
66
67     static defaultResponse<R>(promise: AxiosPromise<R>, actions: ApiActions, mapKeys = true): Promise<R> {
68         const reqId = uuid();
69         actions.progressFn(reqId, true);
70         return promise
71             .then(data => {
72                 actions.progressFn(reqId, false);
73                 return data;
74             })
75             .then((response: { data: any }) => {
76                 return mapKeys ? CommonResourceService.mapResponseKeys(response) : response.data;
77             })
78             .catch(({ response }) => {
79                 actions.progressFn(reqId, false);
80                 const errors = CommonResourceService.mapResponseKeys(response) as Errors;
81                 actions.errorFn(reqId, errors);
82                 throw errors;
83             });
84     }
85
86     protected serverApi: AxiosInstance;
87     protected resourceType: string;
88     protected actions: ApiActions;
89
90     constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
91         this.serverApi = serverApi;
92         this.resourceType = '/' + resourceType + '/';
93         this.actions = actions;
94     }
95
96     create(data?: Partial<T>) {
97         return CommonResourceService.defaultResponse(
98             this.serverApi
99                 .post<T>(this.resourceType, data && CommonResourceService.mapKeys(_.snakeCase)(data)),
100             this.actions
101         );
102     }
103
104     delete(uuid: string): Promise<T> {
105         return CommonResourceService.defaultResponse(
106             this.serverApi
107                 .delete(this.resourceType + uuid),
108             this.actions
109         );
110     }
111
112     get(uuid: string) {
113         return CommonResourceService.defaultResponse(
114             this.serverApi
115                 .get<T>(this.resourceType + uuid),
116             this.actions
117         );
118     }
119
120     list(args: ListArguments = {}): Promise<ListResults<T>> {
121         const { filters, order, ...other } = args;
122         const params = {
123             ...other,
124             filters: filters ? `[${filters}]` : undefined,
125             order: order ? order : undefined
126         };
127         return CommonResourceService.defaultResponse(
128             this.serverApi
129                 .get(this.resourceType, {
130                     params: CommonResourceService.mapKeys(_.snakeCase)(params)
131                 }),
132             this.actions
133         );
134     }
135
136     update(uuid: string, data: Partial<T>) {
137         return CommonResourceService.defaultResponse(
138             this.serverApi
139                 .put<T>(this.resourceType + uuid, data && CommonResourceService.mapKeys(_.snakeCase)(data)),
140             this.actions
141         );
142     }
143 }
144
145 export const getCommonResourceServiceError = (errorResponse: any) => {
146     if ('errors' in errorResponse && 'errorToken' in errorResponse) {
147         const error = errorResponse.errors.join('');
148         switch (true) {
149             case /UniqueViolation/.test(error):
150                 return CommonResourceServiceError.UNIQUE_VIOLATION;
151             case /ownership cycle/.test(error):
152                 return CommonResourceServiceError.OWNERSHIP_CYCLE;
153             case /Mounts cannot be modified in state 'Final'/.test(error):
154                 return CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE;
155             case /Public key does not appear to be a valid ssh-rsa or dsa public key/.test(error):
156                 return CommonResourceServiceError.INVALID_PUBLIC_KEY;
157             case /Public key already exists in the database, use a different key./.test(error):
158                 return CommonResourceServiceError.UNIQUE_PUBLIC_KEY;
159             default:
160                 return CommonResourceServiceError.UNKNOWN;
161         }
162     }
163     return CommonResourceServiceError.NONE;
164 };
165
166