1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import * as _ from "lodash";
6 import { AxiosInstance, AxiosPromise } from "axios";
7 import { Resource } from "src/models/resource";
9 export interface ListArguments {
19 export interface ListResults<T> {
24 itemsAvailable: number;
27 export interface Errors {
32 export enum CommonResourceServiceError {
33 UNIQUE_VIOLATION = 'UniqueViolation',
34 OWNERSHIP_CYCLE = 'OwnershipCycle',
35 MODIFYING_CONTAINER_REQUEST_FINAL_STATE = 'ModifyingContainerRequestFinalState',
40 export class CommonResourceService<T extends Resource> {
42 static mapResponseKeys = (response: { data: any }): Promise<any> =>
43 CommonResourceService.mapKeys(_.camelCase)(response.data)
45 static mapKeys = (mapFn: (key: string) => string) =>
46 (value: any): any => {
48 case _.isPlainObject(value):
51 .map(key => [key, mapFn(key)])
52 .reduce((newValue, [key, newKey]) => ({
54 [newKey]: CommonResourceService.mapKeys(mapFn)(value[key])
56 case _.isArray(value):
57 return value.map(CommonResourceService.mapKeys(mapFn));
63 static defaultResponse<R>(promise: AxiosPromise<R>): Promise<R> {
65 .then(CommonResourceService.mapResponseKeys)
66 .catch(({ response }) => Promise.reject<Errors>(CommonResourceService.mapResponseKeys(response)));
69 protected serverApi: AxiosInstance;
70 protected resourceType: string;
72 constructor(serverApi: AxiosInstance, resourceType: string) {
73 this.serverApi = serverApi;
74 this.resourceType = '/' + resourceType + '/';
77 create(data?: Partial<T> | any) {
78 return CommonResourceService.defaultResponse(
80 .post<T>(this.resourceType, data && CommonResourceService.mapKeys(_.snakeCase)(data)));
83 delete(uuid: string): Promise<T> {
84 return CommonResourceService.defaultResponse(
86 .delete(this.resourceType + uuid));
90 return CommonResourceService.defaultResponse(
92 .get<T>(this.resourceType + uuid));
95 list(args: ListArguments = {}): Promise<ListResults<T>> {
96 const { filters, order, ...other } = args;
99 filters: filters ? `[${filters}]` : undefined,
100 order: order ? order : undefined
102 return CommonResourceService.defaultResponse(
104 .get(this.resourceType, {
105 params: CommonResourceService.mapKeys(_.snakeCase)(params)
109 update(uuid: string, data: Partial<T>) {
110 return CommonResourceService.defaultResponse(
112 .put<T>(this.resourceType + uuid, data && CommonResourceService.mapKeys(_.snakeCase)(data)));
117 export const getCommonResourceServiceError = (errorResponse: any) => {
118 if ('errors' in errorResponse && 'errorToken' in errorResponse) {
119 const error = errorResponse.errors.join('');
121 case /UniqueViolation/.test(error):
122 return CommonResourceServiceError.UNIQUE_VIOLATION;
123 case /ownership cycle/.test(error):
124 return CommonResourceServiceError.OWNERSHIP_CYCLE;
125 case /Mounts cannot be modified in state 'Final'/.test(error):
126 return CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE;
128 return CommonResourceServiceError.UNKNOWN;
131 return CommonResourceServiceError.NONE;