Merge branch 'master'
[arvados-workbench2.git] / src / common / api / 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 { FilterBuilder } from "./filter-builder";
7 import { OrderBuilder } from "./order-builder";
8 import { AxiosInstance, AxiosPromise } from "axios";
9 import { Resource } from "../../models/resource";
10
11 export interface ListArguments {
12     limit?: number;
13     offset?: number;
14     filters?: FilterBuilder;
15     order?: OrderBuilder;
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 class CommonResourceService<T extends Resource> {
35
36     static mapResponseKeys = (response: any): Promise<any> =>
37         CommonResourceService.mapKeys(_.camelCase)(response.data)
38
39     static mapKeys = (mapFn: (key: string) => string) =>
40         (value: any): any => {
41             switch (true) {
42                 case _.isPlainObject(value):
43                     return Object
44                         .keys(value)
45                         .map(key => [key, mapFn(key)])
46                         .reduce((newValue, [key, newKey]) => ({
47                             ...newValue,
48                             [newKey]: CommonResourceService.mapKeys(mapFn)(value[key])
49                         }), {});
50                 case _.isArray(value):
51                     return value.map(CommonResourceService.mapKeys(mapFn));
52                 default:
53                     return value;
54             }
55         }
56
57     static defaultResponse<R>(promise: AxiosPromise<R>): Promise<R> {
58         return promise
59             .then(CommonResourceService.mapResponseKeys)
60             .catch(({ response }) => Promise.reject<Errors>(CommonResourceService.mapResponseKeys(response)));
61     }
62
63     protected serverApi: AxiosInstance;
64     protected resourceType: string;
65
66     constructor(serverApi: AxiosInstance, resourceType: string) {
67         this.serverApi = serverApi;
68         this.resourceType = '/' + resourceType + '/';
69     }
70
71     create(data?: Partial<T> | any) {
72         return CommonResourceService.defaultResponse(
73             this.serverApi
74                 .post<T>(this.resourceType, data && CommonResourceService.mapKeys(_.snakeCase)(data)));
75     }
76
77     delete(uuid: string): Promise<T> {
78         return CommonResourceService.defaultResponse(
79             this.serverApi
80                 .delete(this.resourceType + uuid));
81     }
82
83     get(uuid: string) {
84         return CommonResourceService.defaultResponse(
85             this.serverApi
86                 .get<T>(this.resourceType + uuid));
87     }
88
89     list(args: ListArguments = {}): Promise<ListResults<T>> {
90         const { filters, order, ...other } = args;
91         const params = {
92             ...other,
93             filters: filters ? filters.serialize() : undefined,
94             order: order ? order.getOrder() : undefined
95         };
96         return CommonResourceService.defaultResponse(
97             this.serverApi
98                 .get(this.resourceType, {
99                     params: CommonResourceService.mapKeys(_.snakeCase)(params)
100                 }));
101     }
102
103     update(uuid: string, data: any) {
104         return CommonResourceService.defaultResponse(
105             this.serverApi
106                 .put<T>(this.resourceType + uuid, data));
107
108     }
109 }
110