refs #14744-inappropriate-results
[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     clusterId?: string;
27     kind: string;
28     offset: number;
29     limit: number;
30     items: T[];
31     itemsAvailable: number;
32 }
33
34 export class CommonService<T> {
35     protected serverApi: AxiosInstance;
36     protected resourceType: string;
37     protected actions: ApiActions;
38
39     constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
40         this.serverApi = serverApi;
41         this.resourceType = '/' + resourceType + '/';
42         this.actions = actions;
43     }
44
45     static mapResponseKeys = (response: { data: any }) =>
46         CommonService.mapKeys(_.camelCase)(response.data)
47
48     static mapKeys = (mapFn: (key: string) => string) =>
49         (value: any): any => {
50             switch (true) {
51                 case _.isPlainObject(value):
52                     return Object
53                         .keys(value)
54                         .map(key => [key, mapFn(key)])
55                         .reduce((newValue, [key, newKey]) => ({
56                             ...newValue,
57                             [newKey]: CommonService.mapKeys(mapFn)(value[key])
58                         }), {});
59                 case _.isArray(value):
60                     return value.map(CommonService.mapKeys(mapFn));
61                 default:
62                     return value;
63             }
64         }
65
66     static defaultResponse<R>(promise: AxiosPromise<R>, actions: ApiActions, mapKeys = true): Promise<R> {
67         const reqId = uuid();
68         actions.progressFn(reqId, true);
69         return promise
70             .then(data => {
71                 actions.progressFn(reqId, false);
72                 return data;
73             })
74             .then((response: { data: any }) => {
75                 return mapKeys ? CommonService.mapResponseKeys(response) : response.data;
76             })
77             .catch(({ response }) => {
78                 actions.progressFn(reqId, false);
79                 const errors = CommonService.mapResponseKeys(response) as Errors;
80                 actions.errorFn(reqId, errors);
81                 throw errors;
82             });
83     }
84
85     create(data?: Partial<T>) {
86         return CommonService.defaultResponse(
87             this.serverApi
88                 .post<T>(this.resourceType, data && CommonService.mapKeys(_.snakeCase)(data)),
89             this.actions
90         );
91     }
92
93     delete(uuid: string): Promise<T> {
94         return CommonService.defaultResponse(
95             this.serverApi
96                 .delete(this.resourceType + uuid),
97             this.actions
98         );
99     }
100
101     get(uuid: string) {
102         return CommonService.defaultResponse(
103             this.serverApi
104                 .get<T>(this.resourceType + uuid),
105             this.actions
106         );
107     }
108
109     list(args: ListArguments = {}): Promise<ListResults<T>> {
110         const { filters, order, ...other } = args;
111         const params = {
112             ...other,
113             filters: filters ? `[${filters}]` : undefined,
114             order: order ? order : undefined
115         };
116         return CommonService.defaultResponse(
117             this.serverApi
118                 .get(this.resourceType, {
119                     params: CommonService.mapKeys(_.snakeCase)(params)
120                 }),
121             this.actions
122         );
123     }
124
125     update(uuid: string, data: Partial<T>) {
126         return CommonService.defaultResponse(
127             this.serverApi
128                 .put<T>(this.resourceType + uuid, data && CommonService.mapKeys(_.snakeCase)(data)),
129             this.actions
130         );
131     }
132 }