"@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",
--- /dev/null
+// 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
+ });
+ });
+});
--- /dev/null
+// 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> {
+
+ 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;
+ }
+ }
+
+ protected serverApi: AxiosInstance;
+ protected resourceType: string;
+
+ 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");
+ }
+
+}
+
--- /dev/null
+// 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"]);
+ });
+});
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+import GroupsService from "./groups-service";
+
+describe("GroupsService", () => {
+
+ const axiosMock = new MockAdapter(axios);
+
+ beforeEach(() => {
+ axiosMock.reset();
+ });
+
+ it("#contents", async () => {
+ axiosMock
+ .onGet("/groups/1/contents")
+ .reply(200, {
+ kind: "kind",
+ offset: 2,
+ limit: 10,
+ items: [{
+ modified_at: "now"
+ }],
+ items_available: 20
+ });
+
+ const groupsService = new GroupsService(axios);
+ const resource = await groupsService.contents("1", { limit: 10, offset: 1 });
+ expect(resource).toEqual({
+ kind: "kind",
+ offset: 2,
+ limit: 10,
+ items: [{
+ modifiedAt: "now"
+ }],
+ itemsAvailable: 20
+ });
+ });
+});
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as _ from "lodash";
+import CommonResourceService, { Resource, ListResults } from "../../common/api/common-resource-service";
+import FilterBuilder from "../../common/api/filter-builder";
+import OrderBuilder from "../../common/api/order-builder";
+import { AxiosInstance } from "axios";
+
+interface GroupResource extends Resource {
+ name: string;
+ groupClass: string;
+ description: string;
+ properties: string;
+ writeableBy: string[];
+ trashAt: string;
+ deleteAt: string;
+ isTrashed: boolean;
+}
+
+interface ContensArguments {
+ limit?: number;
+ offset?: number;
+ order?: OrderBuilder;
+ filters?: FilterBuilder;
+ recursive?: boolean;
+}
+
+export default class GroupsService extends CommonResourceService<GroupResource> {
+
+ constructor(serverApi: AxiosInstance) {
+ super(serverApi, "groups");
+ }
+
+ contents (uuid: string, args: ContensArguments = {}): Promise<ListResults<Resource>> {
+ const { filters, order, ...other } = args;
+ const params = {
+ ...other,
+ filters: filters ? filters.get() : undefined,
+ order: order ? order.get() : undefined
+ };
+ return this.serverApi
+ .get(this.resourceType + `${uuid}/contents/`, {
+ params: CommonResourceService.mapKeys(_.snakeCase)(params)
+ })
+ .then(CommonResourceService.mapResponseKeys);
+ }
+}
\ No newline at end of file
import AuthService from "./auth-service/auth-service";
import ProjectService from "./project-service/project-service";
import CollectionService from "./collection-service/collection-service";
+import GroupsService from "./groups-service/groups-service";
+import { serverApi } from "../common/api/server-api";
export const authService = new AuthService();
export const projectService = new ProjectService();
export const collectionService = new CollectionService();
+export const groupsService = new GroupsService(serverApi);
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"