diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..8cf0e41 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.12.0 \ No newline at end of file diff --git a/package.json b/package.json index 99ddcf6..7deb9cf 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,9 @@ "dependencies": { "axios": "^0.21.1", "event-emitter": "^0.3.5", - "form-data": "^3.0.0", "js-base64": "2.6.3", "qs": "^6.10.3", - "urijs": "^1.19.7", - "uuid": "^8.3.2" + "urijs": "^1.19.7" }, "description": "JS client for Put.io API", "devDependencies": { diff --git a/src/client/index.ts b/src/client/index.ts index e3d994f..15d0fe2 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -3,14 +3,12 @@ import qs from 'qs' import { PutioAPIClientResponseInterceptorFactory, - PutioApiClientRequestInterceptorFactory, IPutioAPIClientOptions, IPutioAPIClientResponse, } from './types' import { DEFAULT_CLIENT_OPTIONS } from '../constants' -import { createCorrelationIdSetter } from '../interceptors/request/correlationIdSetter' import { createClientIPChangeEmitter } from '../interceptors/response/clientIPChangeEmitter' import { createErrorEmitter } from '../interceptors/response/errorEmitter' import { createResponseFormatter } from '../interceptors/response/responseFormatter' @@ -180,23 +178,6 @@ export class PutioAPIClient { qs.stringify(params, { arrayFormat: 'comma' }), }) - // apply request interceptors - const requestInterceptorFactories: PutioApiClientRequestInterceptorFactory[] = [ - createCorrelationIdSetter, - ] - - requestInterceptorFactories - .map(createInterceptor => createInterceptor(this.options)) - .forEach(requestInterceptor => { - axiosInstance.interceptors.request.use( - requestInterceptor, - null, - // the 3rd argument is not reflected in the types, but it exists? - // @ts-ignore - { synchronous: true }, - ) - }) - // apply response interceptors const responseInterceptorFactories: PutioAPIClientResponseInterceptorFactory[] = [ createResponseFormatter, diff --git a/src/client/types.ts b/src/client/types.ts index 928c08a..03b28fd 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -1,10 +1,9 @@ -import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios' +import { AxiosError, AxiosResponse } from 'axios' export interface IPutioAPIClientOptions { clientID?: number baseURL?: string webAppURL?: string - generateUUID?: () => string } export interface IPutioAPIClientResponse extends AxiosResponse { @@ -13,7 +12,7 @@ export interface IPutioAPIClientResponse extends AxiosResponse { } export interface IPutioAPIClientErrorData { - correlation_id?: string + 'x-trace-id'?: string error_id?: string error_uri?: string error_type: string @@ -27,14 +26,6 @@ export interface IPutioAPIClientError toJSON: () => IPutioAPIClientErrorData } -export type PutioAPIClientRequestInterceptor = ( - config: AxiosRequestConfig, -) => AxiosRequestConfig - -export type PutioApiClientRequestInterceptorFactory = ( - options: IPutioAPIClientOptions, -) => PutioAPIClientRequestInterceptor - export type PutioAPIClientResponseInterceptor = { onFulfilled: ( response: IPutioAPIClientResponse, diff --git a/src/constants.ts b/src/constants.ts index 382b058..347d8f1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,5 @@ import { IPutioAPIClientOptions } from './client/types' -export const CORRELATION_ID_HEADER_NAME = 'X-Putio-Correlation-Id' -export const NIL_CORRELATION_ID = '00000000-0000-0000-0000-000000000000' export const DEFAULT_CLIENT_OPTIONS: IPutioAPIClientOptions = { baseURL: 'https://api.put.io/v2', clientID: 1, diff --git a/src/interceptors/request/correlationIdSetter.spec.ts b/src/interceptors/request/correlationIdSetter.spec.ts deleted file mode 100644 index 88cf54a..0000000 --- a/src/interceptors/request/correlationIdSetter.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { v4 } from 'uuid' -import { createCorrelationIdSetter } from './correlationIdSetter' -import { - CORRELATION_ID_HEADER_NAME, - NIL_CORRELATION_ID, - DEFAULT_CLIENT_OPTIONS, -} from '../../constants' - -jest.mock('uuid', () => ({ v4: jest.fn() })) - -describe('interceptors/request/correlationIdSetter', () => { - let correlationIdSetter = createCorrelationIdSetter(DEFAULT_CLIENT_OPTIONS) - - beforeEach(() => { - correlationIdSetter = createCorrelationIdSetter(DEFAULT_CLIENT_OPTIONS) - jest.restoreAllMocks() - }) - - it('uses generateUUID function from client options if available', () => { - const mockUUID = 'ecdfa284-6ce1-47b4-b2d4-1d5186fc6f14' - - correlationIdSetter = createCorrelationIdSetter({ - ...DEFAULT_CLIENT_OPTIONS, - generateUUID: () => mockUUID, - }) - - expect( - correlationIdSetter({ headers: {} }).headers[CORRELATION_ID_HEADER_NAME], - ).toBe(mockUUID) - }) - - it('sets a unique correlation id when uuid/v4 works properly in the environment', () => { - const mockUUID = 'fcdfa284-6ce1-47b4-b2d4-1d5186fc6f14' - ;(v4 as jest.Mock).mockImplementationOnce(() => mockUUID) - - expect( - correlationIdSetter({ headers: {} }).headers[CORRELATION_ID_HEADER_NAME], - ).toBe(mockUUID) - }) - - it('uses nil correlation id when uuid/v4 fails to work properly in the environment', () => { - ;(v4 as jest.Mock).mockImplementationOnce(() => { - throw new Error('uuid failed to work in the environment') - }) - - expect( - correlationIdSetter({ headers: {} }).headers[CORRELATION_ID_HEADER_NAME], - ).toBe(NIL_CORRELATION_ID) - }) -}) diff --git a/src/interceptors/request/correlationIdSetter.ts b/src/interceptors/request/correlationIdSetter.ts deleted file mode 100644 index 67d31aa..0000000 --- a/src/interceptors/request/correlationIdSetter.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CORRELATION_ID_HEADER_NAME, NIL_CORRELATION_ID } from '../../constants' -import { PutioApiClientRequestInterceptorFactory } from '../../client/types' - -export const createCorrelationIdSetter: PutioApiClientRequestInterceptorFactory = options => config => { - try { - const { generateUUID } = options - - if (generateUUID) { - config.headers[CORRELATION_ID_HEADER_NAME] = generateUUID() - } else { - const { v4 } = require('uuid') - config.headers[CORRELATION_ID_HEADER_NAME] = v4() - } - } catch { - config.headers[CORRELATION_ID_HEADER_NAME] = NIL_CORRELATION_ID - } - - return config -} diff --git a/src/interceptors/response/responseFormatter.spec.ts b/src/interceptors/response/responseFormatter.spec.ts index 826eb25..cc9a65c 100644 --- a/src/interceptors/response/responseFormatter.spec.ts +++ b/src/interceptors/response/responseFormatter.spec.ts @@ -18,16 +18,14 @@ describe('interceptors/response/responseFormatter', () => { "foo": "bar", "status": "OK", }, - "config": Object { - "headers": Object { - "X-Putio-Correlation-Id": "00000000-0000-0000-0000-000000000000", - }, - }, + "config": Object {}, "data": Object { "foo": "bar", "status": "OK", }, - "headers": Object {}, + "headers": Object { + "x-trace-id": "MOCK_TRACE_ID", + }, "status": 200, "statusText": "ok", } @@ -46,7 +44,9 @@ describe('interceptors/response/responseFormatter', () => { error_message: 'Putio API Error', status_code: 400, }, - headers: {}, + headers: { + 'x-trace-id': 'MOCK_TRACE_ID', + }, status: 400, statusText: 'Error!', }, @@ -55,10 +55,10 @@ describe('interceptors/response/responseFormatter', () => { responseFormatter.onRejected(error).catch(e => expect(e).toMatchInlineSnapshot(` Object { - "correlation_id": "00000000-0000-0000-0000-000000000000", "error_message": "Putio API Error", "error_type": "API_ERROR", "status_code": 400, + "x-trace-id": "MOCK_TRACE_ID", } `), ) @@ -81,10 +81,10 @@ describe('interceptors/response/responseFormatter', () => { responseFormatter.onRejected(error).catch(e => expect(e).toMatchInlineSnapshot(` Object { - "correlation_id": undefined, "error_message": "AXIOS_ERROR_MESSAGE", "error_type": "ERROR", "status_code": 502, + "x-trace-id": undefined, } `), ) @@ -94,10 +94,10 @@ describe('interceptors/response/responseFormatter', () => { responseFormatter.onRejected(mockPutioAPIClientError).catch(e => expect(e).toMatchInlineSnapshot(` Object { - "correlation_id": undefined, "error_message": "AXIOS_ERROR_MESSAGE", "error_type": "ERROR", "status_code": 0, + "x-trace-id": undefined, } `), ) @@ -134,11 +134,11 @@ describe('interceptors/response/responseFormatter', () => { responseFormatter.onRejected(error).catch(e => expect(e).toMatchInlineSnapshot(` Object { - "correlation_id": undefined, "error_message": "AXIOS_ERROR_MESSAGE", "error_type": "ERROR", "foo": "bar", "status_code": 400, + "x-trace-id": undefined, } `), ) diff --git a/src/interceptors/response/responseFormatter.ts b/src/interceptors/response/responseFormatter.ts index 6fef3d2..a0db9cc 100644 --- a/src/interceptors/response/responseFormatter.ts +++ b/src/interceptors/response/responseFormatter.ts @@ -4,7 +4,6 @@ import { IPutioAPIClientErrorData, PutioAPIClientResponseInterceptorFactory, } from '../../client/types' -import { CORRELATION_ID_HEADER_NAME } from '../../constants' import { isPutioAPIErrorResponse } from '../../utils' export const createResponseFormatter: PutioAPIClientResponseInterceptorFactory = () => ({ @@ -19,7 +18,7 @@ export const createResponseFormatter: PutioAPIClientResponseInterceptorFactory = } let errorData: IPutioAPIClientErrorData = { - correlation_id: error.config.headers?.[CORRELATION_ID_HEADER_NAME], + 'x-trace-id': error.response?.headers['x-trace-id'], error_message: error.message, error_type: 'ERROR', status_code: 0, diff --git a/src/resources/Files/Files.spec.ts b/src/resources/Files/Files.spec.ts new file mode 100644 index 0000000..cb61bc2 --- /dev/null +++ b/src/resources/Files/Files.spec.ts @@ -0,0 +1,20 @@ +import PutioAPIClient, { isPutioAPIError } from '../../index' + +describe('resources/Files/Files', () => { + const API = new PutioAPIClient({}) + + it('should construct correct payload for Upload method', async () => { + try { + API.setToken('test-token') + + await API.Files.Upload({ + file: new File([], 'test'), + parentId: 0, + }) + } catch (e) { + if (isPutioAPIError(e)) { + expect(e.config.headers['Authorization']).toBe(`token test-token`) + } + } + }) +}) diff --git a/src/resources/Files/Files.ts b/src/resources/Files/Files.ts index 7fd0b3a..267ec7c 100644 --- a/src/resources/Files/Files.ts +++ b/src/resources/Files/Files.ts @@ -318,15 +318,22 @@ export default class Files { fileName?: string parentId?: number }) { - const FormData = require('form-data') const form = new FormData() form.append('file', file) - form.append('filename', fileName) - form.append('parent_id', parentId) + + if (fileName) { + form.append('filename', fileName) + } + + if (parentId) { + form.append('parent_id', parentId.toString()) + } return this.client.post('/files/upload', { data: form, - headers: form.getHeaders(), + headers: { + 'Content-Type': 'multipart/form-data', + }, }) } } diff --git a/src/test-utils/mocks.ts b/src/test-utils/mocks.ts index 60c1b79..ca62ff4 100644 --- a/src/test-utils/mocks.ts +++ b/src/test-utils/mocks.ts @@ -1,23 +1,20 @@ import { AxiosError, AxiosRequestConfig } from 'axios' -import { CORRELATION_ID_HEADER_NAME, NIL_CORRELATION_ID } from '../constants' import { IPutioAPIClientError, IPutioAPIClientErrorData, IPutioAPIClientResponse, } from '../client/types' -const mockRequestConfig: AxiosRequestConfig = { - headers: { - [CORRELATION_ID_HEADER_NAME]: NIL_CORRELATION_ID, - }, -} +const mockRequestConfig: AxiosRequestConfig = {} export const mockPutioAPIClientResponse: IPutioAPIClientResponse<{ foo: string }> = { config: mockRequestConfig, data: { foo: 'bar', status: 'OK' }, - headers: {}, + headers: { + 'x-trace-id': 'MOCK_TRACE_ID', + }, status: 200, statusText: 'ok', } diff --git a/yarn.lock b/yarn.lock index 6be3a24..649447b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2228,7 +2228,7 @@ colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3194,15 +3194,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -6527,11 +6518,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache@^2.0.3: version "2.1.1" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"