Create CommonResourceService and OrderBuilder
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 2 Jul 2018 09:09:21 +0000 (11:09 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 2 Jul 2018 09:09:21 +0000 (11:09 +0200)
Feature #13702

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

package.json
src/common/api/common-resource-service.test.ts [new file with mode: 0644]
src/common/api/common-resource-service.ts [new file with mode: 0644]
src/common/api/order-builder.test.ts [new file with mode: 0644]
src/common/api/order-builder.ts [new file with mode: 0644]
yarn.lock

index 3db240455871d95eecbcb89bb0014d0488a9ebea..f769acc98ff5c2ebeedb95c9e3e23fc1a5002ee4 100644 (file)
@@ -40,6 +40,7 @@
     "@types/react-router-dom": "4.2.7",
     "@types/react-router-redux": "5.0.15",
     "@types/redux-devtools": "3.0.44",
+    "axios-mock-adapter": "^1.15.0",
     "enzyme": "^3.3.0",
     "enzyme-adapter-react-16": "^1.1.1",
     "jest-localstorage-mock": "2.2.0",
diff --git a/src/common/api/common-resource-service.test.ts b/src/common/api/common-resource-service.test.ts
new file mode 100644 (file)
index 0000000..a07655e
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import CommonResourceService from "./common-resource-service";
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+
+describe("CommonResourceService", () => {
+
+    const axiosMock = new MockAdapter(axios);
+
+    beforeEach(() => {
+        axiosMock.reset();
+    });
+
+    it("#delete", async () => {
+        axiosMock
+            .onDelete("/resource/uuid")
+            .reply(200, { deleted_at: "now" });
+
+        const commonResourceService = new CommonResourceService(axios, "resource");
+        const resource = await commonResourceService.delete("uuid");
+        expect(resource).toEqual({ deletedAt: "now" });
+    });
+
+    it("#get", async () => {
+        axiosMock
+            .onGet("/resource/uuid")
+            .reply(200, { modified_at: "now" });
+
+        const commonResourceService = new CommonResourceService(axios, "resource");
+        const resource = await commonResourceService.get("uuid");
+        expect(resource).toEqual({ modifiedAt: "now" });
+    });
+
+    it("#list", async () => {
+        axiosMock
+            .onGet("/resource")
+            .reply(200, {
+                kind: "kind",
+                offset: 2,
+                limit: 10,
+                items: [{
+                    modified_at: "now"
+                }],
+                items_available: 20
+            });
+
+        const commonResourceService = new CommonResourceService(axios, "resource");
+        const resource = await commonResourceService.list({ limit: 10, offset: 1 });
+        expect(resource).toEqual({
+            kind: "kind",
+            offset: 2,
+            limit: 10,
+            items: [{
+                modifiedAt: "now"
+            }],
+            itemsAvailable: 20
+        });
+    });
+});
diff --git a/src/common/api/common-resource-service.ts b/src/common/api/common-resource-service.ts
new file mode 100644 (file)
index 0000000..e49d2a0
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as _ from "lodash";
+import FilterBuilder from "./filter-builder";
+import OrderBuilder from "./order-builder";
+import { AxiosInstance } from "axios";
+
+export interface Resource {
+    uuid: string;
+    ownerUuid: string;
+    createdAt: string;
+    modifiedByClientUuid: string;
+    modifiedByUserUuid: string;
+    modifiedAt: string;
+    href: string;
+    kind: string;
+    etag: string;
+}
+
+export interface ListArguments {
+    limit: number;
+    offset: number;
+    filters?: FilterBuilder;
+    order?: OrderBuilder;
+    select?: string[];
+    distinct?: boolean;
+    count?: string;
+}
+
+export interface ListResults<T> {
+    kind: string;
+    offset: number;
+    limit: number;
+    items: T[];
+    itemsAvailable: number;
+}
+
+export default class CommonResourceService<T extends Resource> {
+
+    serverApi: AxiosInstance;
+    resourceType: string;
+
+    static mapResponseKeys = (response: any): Promise<any> =>
+        CommonResourceService.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]: CommonResourceService.mapKeys(mapFn)(value[key])
+                        }), {});
+                case _.isArray(value):
+                    return value.map(CommonResourceService.mapKeys(mapFn));
+                default:
+                    return value;
+            }
+        }
+
+    constructor(serverApi: AxiosInstance, resourceType: string) {
+        this.serverApi = serverApi;
+        this.resourceType = "/" + resourceType;
+    }
+
+    create() {
+        throw new Error("Not implemented");
+    }
+
+    delete(uuid: string): Promise<T> {
+        return this.serverApi
+            .delete(this.resourceType + "/" + uuid)
+            .then(CommonResourceService.mapResponseKeys);
+    }
+
+    get(uuid: string) {
+        return this.serverApi
+            .get<T>(this.resourceType + "/" + uuid)
+            .then(CommonResourceService.mapResponseKeys);
+    }
+
+    list(args: ListArguments): Promise<ListResults<T>> {
+        const { filters, order, ...other } = args;
+        const params = {
+            ...other,
+            filters: filters ? filters.get() : undefined,
+            order: order ? order.get() : undefined
+        };
+        return this.serverApi
+            .get(this.resourceType, {
+                params: CommonResourceService.mapKeys(_.snakeCase)(params)
+            })
+            .then(CommonResourceService.mapResponseKeys);
+    }
+
+    update(uuid: string) {
+        throw new Error("Not implemented");
+    }
+
+}
+
diff --git a/src/common/api/order-builder.test.ts b/src/common/api/order-builder.test.ts
new file mode 100644 (file)
index 0000000..7647426
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import OrderBuilder from "./order-builder";
+
+describe("OrderBuilder", () => {
+    it("should build correct order query", () => {
+        const orderBuilder = new OrderBuilder();
+        const order = orderBuilder
+            .addAsc("name")
+            .addDesc("modified_at")
+            .get();
+        expect(order).toEqual(`["name asc","modified_at desc"]`);
+    });
+});
diff --git a/src/common/api/order-builder.ts b/src/common/api/order-builder.ts
new file mode 100644 (file)
index 0000000..394cce5
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+
+export default class OrderBuilder {
+    private order: string[] = [];
+
+    addAsc(attribute: string) {
+        this.order.push(`${attribute} asc`);
+        return this;
+    }
+
+    addDesc(attribute: string) {
+        this.order.push(`${attribute} desc`);
+        return this;
+    }
+
+    get() {
+        return `["${this.order.join(`","`)}"]`;
+    }
+}
index 9a3379b13a4082d1095a389ee2c1551c9bbff1ab..d71a56150c9eebd9558f20fcdb3813561226315f 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -477,6 +477,12 @@ aws4@^1.6.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"
 
+axios-mock-adapter@^1.15.0:
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.15.0.tgz#fbc06825d8302c95c3334d21023bba996255d45d"
+  dependencies:
+    deep-equal "^1.0.1"
+
 axios@0.18.0:
   version "0.18.0"
   resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"