Merge branch 'origin/master' into 14478-log-in-into-clusters
[arvados-workbench2.git] / src / services / common-service / common-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 * as uuid from "uuid/v4";
8 import { ApiActions } from "~/services/api/api-actions";
9
10 interface Errors {
11     errors: string[];
12     errorToken: string;
13 }
14
15 export interface ListArguments {
16     limit?: number;
17     offset?: number;
18     filters?: string;
19     order?: string;
20     select?: string[];
21     distinct?: boolean;
22     count?: string;
23 }
24
25 export interface ListResults<T> {
26     kind: string;
27     offset: number;
28     limit: number;
29     items: T[];
30     itemsAvailable: number;
31 }
32
33 export class CommonService<T> {
34     protected serverApi: AxiosInstance;
35     protected resourceType: string;
36     protected actions: ApiActions;
37
38     constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
39         this.serverApi = serverApi;
40         this.resourceType = '/' + resourceType + '/';
41         this.actions = actions;
42     }
43
44     static mapResponseKeys = (response: { data: any }) =>
45         CommonService.mapKeys(_.camelCase)(response.data)
46
47     static mapKeys = (mapFn: (key: string) => string) =>
48         (value: any): any => {
49             switch (true) {
50                 case _.isPlainObject(value):
51                     return Object
52                         .keys(value)
53                         .map(key => [key, mapFn(key)])
54                         .reduce((newValue, [key, newKey]) => ({
55                             ...newValue,
56                             [newKey]: CommonService.mapKeys(mapFn)(value[key])
57                         }), {});
58                 case _.isArray(value):
59                     return value.map(CommonService.mapKeys(mapFn));
60                 default:
61                     return value;
62             }
63         }
64
65     static defaultResponse<R>(promise: AxiosPromise<R>, actions: ApiActions, mapKeys = true): Promise<R> {
66         const reqId = uuid();
67         actions.progressFn(reqId, true);
68         return promise
69             .then(data => {
70                 actions.progressFn(reqId, false);
71                 return data;
72             })
73             .then((response: { data: any }) => {
74                 return mapKeys ? CommonService.mapResponseKeys(response) : response.data;
75             })
76             .catch(({ response }) => {
77                 actions.progressFn(reqId, false);
78                 const errors = CommonService.mapResponseKeys(response) as Errors;
79                 actions.errorFn(reqId, errors);
80                 throw errors;
81             });
82     }
83
84     create(data?: Partial<T>) {
85         return CommonService.defaultResponse(
86             this.serverApi
87                 .post<T>(this.resourceType, data && CommonService.mapKeys(_.snakeCase)(data)),
88             this.actions
89         );
90     }
91
92     delete(uuid: string): Promise<T> {
93         return CommonService.defaultResponse(
94             this.serverApi
95                 .delete(this.resourceType + uuid),
96             this.actions
97         );
98     }
99
100     get(uuid: string) {
101         return CommonService.defaultResponse(
102             this.serverApi
103                 .get<T>(this.resourceType + uuid),
104             this.actions
105         );
106     }
107
108     list(args: ListArguments = {}): Promise<ListResults<T>> {
109         const { filters, order, ...other } = args;
110         const params = {
111             ...other,
112             filters: filters ? `[${filters}]` : undefined,
113             order: order ? order : undefined
114         };
115         return CommonService.defaultResponse(
116             this.serverApi
117                 .get(this.resourceType, {
118                     params: CommonService.mapKeys(_.snakeCase)(params)
119                 }),
120             this.actions
121         );
122     }
123
124     update(uuid: string, data: Partial<T>) {
125         return CommonService.defaultResponse(
126             this.serverApi
127                 .put<T>(this.resourceType + uuid, data && CommonService.mapKeys(_.snakeCase)(data)),
128             this.actions
129         );
130     }
131 }