diff --git a/package-lock.json b/package-lock.json index 72386041e..b73cb3487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@crowdin/crowdin-api-client", - "version": "1.17.1", + "version": "1.18.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d1107a91d..e291f650d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@crowdin/crowdin-api-client", - "version": "1.17.1", + "version": "1.18.0", "description": "JavaScript library for Crowdin API v2.", "main": "out/index.js", "types": "out/index.d.ts", diff --git a/src/core/index.ts b/src/core/index.ts index 24c2570cd..00358d02b 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -43,28 +43,6 @@ export interface Pagination { export type PaginationOptions = Partial; -export interface ValidationErrorResponse { - errors: ErrorHolder[]; -} - -export interface CommonErrorResponse { - error: Error; -} - -export interface ErrorHolder { - error: ErrorKey; -} - -export interface ErrorKey { - key: string; - errors: Error[]; -} - -export interface Error { - code: string; - message: string; -} - export interface PatchRequest { value?: any; op: PatchOperation; @@ -98,6 +76,44 @@ export interface Attribute { [key: string]: string; } +export class CrowdinError extends Error { + public code: number; + constructor(message: string, code: number) { + super(message); + this.code = code; + } +} + +export class CrowdinValidationError extends CrowdinError { + public validationCodes: string[]; + constructor(messsage: string, validationCodes: string[]) { + super(messsage, 400); + this.validationCodes = validationCodes; + } +} + +function handleError(error: any = {}): T { + if (Array.isArray(error.errors)) { + const validationCodes: string[] = []; + const validationMessages: string[] = []; + error.errors.forEach((e: any) => { + if (Array.isArray(e.error?.errors)) { + e.error.errors.forEach((er: any) => { + if (er.message && er.code) { + validationCodes.push(er.code); + validationMessages.push(er.message); + } + }); + } + }); + const message = validationMessages.length === 0 ? 'Validation error' : validationMessages.join(', '); + throw new CrowdinValidationError(message, validationCodes); + } + const message = error.error?.message || 'Error occured'; + const code = error.error?.code || 500; + throw new CrowdinError(message, code); +} + export abstract class CrowdinApi { private static readonly CROWDIN_URL_SUFFIX: string = 'api.crowdin.com/api/v2'; private static readonly AXIOS_INSTANCE = new AxiosProvider().axios; @@ -249,27 +265,37 @@ export abstract class CrowdinApi { //Http overrides protected get(url: string, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.get(url, config)); + return this.retryService.executeAsyncFunc(() => this.httpClient.get(url, config).catch(e => handleError(e))); } protected delete(url: string, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.delete(url, config)); + return this.retryService.executeAsyncFunc(() => + this.httpClient.delete(url, config).catch(e => handleError(e)), + ); } protected head(url: string, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.head(url, config)); + return this.retryService.executeAsyncFunc(() => + this.httpClient.head(url, config).catch(e => handleError(e)), + ); } protected post(url: string, data?: unknown, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.post(url, data, config)); + return this.retryService.executeAsyncFunc(() => + this.httpClient.post(url, data, config).catch(e => handleError(e)), + ); } protected put(url: string, data?: unknown, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.put(url, data, config)); + return this.retryService.executeAsyncFunc(() => + this.httpClient.put(url, data, config).catch(e => handleError(e)), + ); } protected patch(url: string, data?: unknown, config?: { headers: Record }): Promise { - return this.retryService.executeAsyncFunc(() => this.httpClient.patch(url, data, config)); + return this.retryService.executeAsyncFunc(() => + this.httpClient.patch(url, data, config).catch(e => handleError(e)), + ); } } diff --git a/src/core/internal/axios/axiosProvider.ts b/src/core/internal/axios/axiosProvider.ts index 4b48e0a29..29a8cc152 100644 --- a/src/core/internal/axios/axiosProvider.ts +++ b/src/core/internal/axios/axiosProvider.ts @@ -1,5 +1,4 @@ import axios, { AxiosInstance } from 'axios'; -import { CommonErrorResponse, ValidationErrorResponse } from '../..'; export class AxiosProvider { private static readonly CROWDIN_API_MAX_CONCURRENT_REQUESTS = 15; @@ -36,22 +35,7 @@ export class AxiosProvider { }, error => { this.pendingRequests = Math.max(0, this.pendingRequests - 1); - if (error.response?.data) { - if (error.response.status === 400) { - return Promise.reject(error.response.data as ValidationErrorResponse); - } else { - return Promise.reject(error.response.data as CommonErrorResponse); - } - } else { - const errorCode = error.response?.status ?? '500'; - const defaultError: CommonErrorResponse = { - error: { - code: errorCode, - message: `Request failed. ${error}`, - }, - }; - return Promise.reject(defaultError); - } + return Promise.reject(error.response.data); }, ); }