Merge branch '14500_admin_api_tokens'
[arvados-workbench2.git] / src / services / common-service / common-service.ts
diff --git a/src/services/common-service/common-service.ts b/src/services/common-service/common-service.ts
new file mode 100644 (file)
index 0000000..b301a72
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as _ from "lodash";
+import { AxiosInstance, AxiosPromise } from "axios";
+import * as uuid from "uuid/v4";
+import { ApiActions } from "~/services/api/api-actions";
+
+interface Errors {
+    errors: string[];
+    errorToken: string;
+}
+
+export interface ListArguments {
+    limit?: number;
+    offset?: number;
+    filters?: string;
+    order?: string;
+    select?: string[];
+    distinct?: boolean;
+    count?: string;
+}
+
+export interface ListResults<T> {
+    kind: string;
+    offset: number;
+    limit: number;
+    items: T[];
+    itemsAvailable: number;
+}
+
+export class CommonService<T> {
+    protected serverApi: AxiosInstance;
+    protected resourceType: string;
+    protected actions: ApiActions;
+
+    constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
+        this.serverApi = serverApi;
+        this.resourceType = '/' + resourceType + '/';
+        this.actions = actions;
+    }
+
+    static mapResponseKeys = (response: { data: any }) =>
+        CommonService.mapKeys(_.camelCase)(response.data)
+
+    static mapKeys = (mapFn: (key: string) => string) =>
+        (value: any): any => {
+            switch (true) {
+                case _.isPlainObject(value):
+                    return Object
+                        .keys(value)
+                        .map(key => [key, mapFn(key)])
+                        .reduce((newValue, [key, newKey]) => ({
+                            ...newValue,
+                            [newKey]: CommonService.mapKeys(mapFn)(value[key])
+                        }), {});
+                case _.isArray(value):
+                    return value.map(CommonService.mapKeys(mapFn));
+                default:
+                    return value;
+            }
+        }
+
+    static defaultResponse<R>(promise: AxiosPromise<R>, actions: ApiActions, mapKeys = true): Promise<R> {
+        const reqId = uuid();
+        actions.progressFn(reqId, true);
+        return promise
+            .then(data => {
+                actions.progressFn(reqId, false);
+                return data;
+            })
+            .then((response: { data: any }) => {
+                return mapKeys ? CommonService.mapResponseKeys(response) : response.data;
+            })
+            .catch(({ response }) => {
+                actions.progressFn(reqId, false);
+                const errors = CommonService.mapResponseKeys(response) as Errors;
+                actions.errorFn(reqId, errors);
+                throw errors;
+            });
+    }
+
+    create(data?: Partial<T>) {
+        return CommonService.defaultResponse(
+            this.serverApi
+                .post<T>(this.resourceType, data && CommonService.mapKeys(_.snakeCase)(data)),
+            this.actions
+        );
+    }
+
+    delete(uuid: string): Promise<T> {
+        return CommonService.defaultResponse(
+            this.serverApi
+                .delete(this.resourceType + uuid),
+            this.actions
+        );
+    }
+
+    get(uuid: string) {
+        return CommonService.defaultResponse(
+            this.serverApi
+                .get<T>(this.resourceType + uuid),
+            this.actions
+        );
+    }
+
+    list(args: ListArguments = {}): Promise<ListResults<T>> {
+        const { filters, order, ...other } = args;
+        const params = {
+            ...other,
+            filters: filters ? `[${filters}]` : undefined,
+            order: order ? order : undefined
+        };
+        return CommonService.defaultResponse(
+            this.serverApi
+                .get(this.resourceType, {
+                    params: CommonService.mapKeys(_.snakeCase)(params)
+                }),
+            this.actions
+        );
+    }
+
+    update(uuid: string, data: Partial<T>) {
+        return CommonService.defaultResponse(
+            this.serverApi
+                .put<T>(this.resourceType + uuid, data && CommonService.mapKeys(_.snakeCase)(data)),
+            this.actions
+        );
+    }
+}
\ No newline at end of file