From d3f17ba544b22ae76f47401041362bbcefe95aaa Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Mon, 2 Jul 2018 11:09:21 +0200 Subject: [PATCH] Create CommonResourceService and OrderBuilder Feature #13702 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- package.json | 1 + .../api/common-resource-service.test.ts | 62 ++++++++++ src/common/api/common-resource-service.ts | 106 ++++++++++++++++++ src/common/api/order-builder.test.ts | 16 +++ src/common/api/order-builder.ts | 22 ++++ yarn.lock | 6 + 6 files changed, 213 insertions(+) create mode 100644 src/common/api/common-resource-service.test.ts create mode 100644 src/common/api/common-resource-service.ts create mode 100644 src/common/api/order-builder.test.ts create mode 100644 src/common/api/order-builder.ts diff --git a/package.json b/package.json index 3db24045..f769acc9 100644 --- a/package.json +++ b/package.json @@ -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 index 00000000..a07655e5 --- /dev/null +++ b/src/common/api/common-resource-service.test.ts @@ -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 index 00000000..e49d2a0b --- /dev/null +++ b/src/common/api/common-resource-service.ts @@ -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 { + kind: string; + offset: number; + limit: number; + items: T[]; + itemsAvailable: number; +} + +export default class CommonResourceService { + + serverApi: AxiosInstance; + resourceType: string; + + static mapResponseKeys = (response: any): Promise => + 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 { + return this.serverApi + .delete(this.resourceType + "/" + uuid) + .then(CommonResourceService.mapResponseKeys); + } + + get(uuid: string) { + return this.serverApi + .get(this.resourceType + "/" + uuid) + .then(CommonResourceService.mapResponseKeys); + } + + list(args: ListArguments): Promise> { + 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 index 00000000..7647426b --- /dev/null +++ b/src/common/api/order-builder.test.ts @@ -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 index 00000000..394cce5c --- /dev/null +++ b/src/common/api/order-builder.ts @@ -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(`","`)}"]`; + } +} diff --git a/yarn.lock b/yarn.lock index 9a3379b1..d71a5615 100644 --- 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" -- 2.30.2