X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/db1c226e212aaafff8f8d1c6ddfb092e247db5fe..HEAD:/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 index 8e00c4ad..8e9fe631 100644 --- a/src/services/common-service/common-service.ts +++ b/src/services/common-service/common-service.ts @@ -2,11 +2,12 @@ // // 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"; -import * as QueryString from "query-string"; +import { camelCase, isPlainObject, isArray, snakeCase } from "lodash"; +import { AxiosInstance, AxiosPromise, AxiosRequestConfig } from "axios"; +import uuid from "uuid/v4"; +import { ApiActions } from "services/api/api-actions"; +import QueryString from "query-string"; +import { Session } from "models/session"; interface Errors { status: number; @@ -42,18 +43,18 @@ export class CommonService { constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions, readOnlyFields: string[] = []) { this.serverApi = serverApi; - this.resourceType = '/' + resourceType; + this.resourceType = resourceType; this.actions = actions; this.readOnlyFields = readOnlyFields; } static mapResponseKeys = (response: { data: any }) => - CommonService.mapKeys(_.camelCase)(response.data) + CommonService.mapKeys(camelCase)(response.data) static mapKeys = (mapFn: (key: string) => string) => (value: any): any => { switch (true) { - case _.isPlainObject(value): + case isPlainObject(value): return Object .keys(value) .map(key => [key, mapFn(key)]) @@ -61,13 +62,19 @@ export class CommonService { ...newValue, [newKey]: (key === 'items') ? CommonService.mapKeys(mapFn)(value[key]) : value[key] }), {}); - case _.isArray(value): + case isArray(value): return value.map(CommonService.mapKeys(mapFn)); default: return value; } } + protected validateUuid(uuid: string) { + if (uuid === "") { + throw new Error('UUID cannot be empty string'); + } + } + static defaultResponse(promise: AxiosPromise, actions: ApiActions, mapKeys = true, showErrors = true): Promise { const reqId = uuid(); actions.progressFn(reqId, true); @@ -80,78 +87,106 @@ export class CommonService { return mapKeys ? CommonService.mapResponseKeys(response) : response.data; }) .catch(({ response }) => { - actions.progressFn(reqId, false); - const errors = CommonService.mapResponseKeys(response) as Errors; - errors.status = response.status; - actions.errorFn(reqId, errors, showErrors); - throw errors; + if (response) { + actions.progressFn(reqId, false); + const errors = CommonService.mapResponseKeys(response) as Errors; + errors.status = response.status; + actions.errorFn(reqId, errors, showErrors); + throw errors; + } }); } - create(data?: Partial) { + create(data?: Partial, showErrors?: boolean) { return CommonService.defaultResponse( this.serverApi - .post(this.resourceType, data && CommonService.mapKeys(_.snakeCase)(data)), - this.actions + .post(`/${this.resourceType}`, data && CommonService.mapKeys(snakeCase)(data)), + this.actions, + true, // mapKeys + showErrors ); } - delete(uuid: string): Promise { + delete(uuid: string, showErrors?: boolean): Promise { + this.validateUuid(uuid); return CommonService.defaultResponse( this.serverApi - .delete(this.resourceType + '/' + uuid), - this.actions + .delete(`/${this.resourceType}/${uuid}`), + this.actions, + true, // mapKeys + showErrors ); } - get(uuid: string, showErrors?: boolean) { + get(uuid: string, showErrors?: boolean, select?: string[], session?: Session) { + this.validateUuid(uuid); + + const cfg: AxiosRequestConfig = { + params: { + select: select + ? `[${select.map(snakeCase).map(s => `"${s}"`).join(',')}]` + : undefined + } + }; + if (session) { + cfg.baseURL = session.baseUrl; + cfg.headers = { 'Authorization': 'Bearer ' + session.token }; + } + return CommonService.defaultResponse( this.serverApi - .get(this.resourceType + '/' + uuid), + .get(`/${this.resourceType}/${uuid}`, cfg), this.actions, true, // mapKeys showErrors ); } - list(args: ListArguments = {}): Promise> { - const { filters, order, ...other } = args; + list(args: ListArguments = {}, showErrors?: boolean): Promise> { + const { filters, select, ...other } = args; const params = { - ...CommonService.mapKeys(_.snakeCase)(other), + ...CommonService.mapKeys(snakeCase)(other), filters: filters ? `[${filters}]` : undefined, - order: order ? order : undefined + select: select + ? `[${select.map(snakeCase).map(s => `"${s}"`).join(', ')}]` + : undefined }; if (QueryString.stringify(params).length <= 1500) { return CommonService.defaultResponse( - this.serverApi.get(this.resourceType, { params }), - this.actions + this.serverApi.get(`/${this.resourceType}`, { params }), + this.actions, + true, + showErrors ); } else { // Using the POST special case to avoid URI length 414 errors. - const formData = new FormData(); + // We must use urlencoded post body since api doesn't support form data + // const formData = new FormData(); + const formData = new URLSearchParams(); formData.append("_method", "GET"); - Object.keys(params).map(key => { + Object.keys(params).forEach(key => { if (params[key] !== undefined) { formData.append(key, params[key]); } }); return CommonService.defaultResponse( - this.serverApi.post(this.resourceType, formData, { - params: { - _method: 'GET' - } - }), - this.actions + this.serverApi.post(`/${this.resourceType}`, formData, {}), + this.actions, + true, + showErrors ); } } - update(uuid: string, data: Partial) { + update(uuid: string, data: Partial, showErrors?: boolean) { + this.validateUuid(uuid); return CommonService.defaultResponse( this.serverApi - .put(this.resourceType + '/' + uuid, data && CommonService.mapKeys(_.snakeCase)(data)), - this.actions + .put(`/${this.resourceType}/${uuid}`, data && CommonService.mapKeys(snakeCase)(data)), + this.actions, + undefined, // mapKeys + showErrors ); } }