diff --git a/packages/clients/src/scw/fetch/__tests__/response-parser.ts b/packages/clients/src/scw/fetch/__tests__/response-parser.ts index 9fe082a13..7b04bcd5d 100644 --- a/packages/clients/src/scw/fetch/__tests__/response-parser.ts +++ b/packages/clients/src/scw/fetch/__tests__/response-parser.ts @@ -6,7 +6,7 @@ import { fixLegacyTotalCount, responseParser } from '../response-parser' const SIMPLE_REQ_BODY = { 'what-is-life': 42 } -const convertObjToBuffer = (obj: JSON): Buffer => { +const convertObjToBuffer = (obj: unknown): Buffer => { const str = JSON.stringify(obj) const bytes = new TextEncoder().encode(str) @@ -19,24 +19,35 @@ const unmarshalJSON = (obj: unknown) => { return obj } -const makeValidJSONResponse = ( - value: JSON | null = SIMPLE_REQ_BODY, +const makeResponse = ( + value: unknown, status = 200, + contentType: string | undefined = undefined, ) => new Response(value !== null ? convertObjToBuffer(value) : value, { - headers: { 'Content-Type': 'application/json' }, + headers: contentType ? { 'Content-Type': contentType } : undefined, status, }) -const makeValidTextResponse = (value: string | null, status = 200) => +const makeJSONResponse = (value: JSON = SIMPLE_REQ_BODY, status = 200) => + makeResponse(value, status, 'application/json') + +const makeTextResponse = (value: string, status = 200) => new Response(value, { headers: { 'Content-Type': 'plain/text' }, status, }) describe(`responseParser`, () => { - const parseJson = responseParser(unmarshalJSON) - const parseAsIs = responseParser((response: unknown) => response as T) + const parseJson = responseParser(unmarshalJSON, 'json') + const parseAsIs = responseParser( + (response: unknown) => response as T, + 'json', + ) + const parseBlob = responseParser( + (response: unknown) => response as T, + 'blob', + ) it(`triggers a type error for non 'Response' object`, () => expect( @@ -87,12 +98,13 @@ describe(`responseParser`, () => { }) it(`triggers an error for unsuccessful unmarshalling`, async () => { - const validResponse = makeValidJSONResponse() + const validResponse = makeJSONResponse() + const emptyContentTypeResponse = makeResponse('some-text', 200, '') await expect( responseParser(() => { throw new Error(`couldn't unwrap response value`) - })(validResponse.clone()), + }, 'json')(validResponse.clone()), ).rejects.toThrow( new ScalewayError( validResponse.status, @@ -104,30 +116,42 @@ describe(`responseParser`, () => { responseParser(() => { // eslint-disable-next-line @typescript-eslint/no-throw-literal throw 'not-of-error-type' - })(validResponse.clone()), + }, 'text')(validResponse.clone()), ).rejects.toThrow( new ScalewayError( validResponse.status, `could not parse 'application/json' response`, ), ) - }) - it(`returns the response as-if for unknown content type`, async () => { - const textResponse = makeValidTextResponse('text-body') - - return expect(parseAsIs(textResponse)).resolves.toBe('text-body') + await expect( + responseParser(() => { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw 'not-of-error-type' + }, 'blob')(emptyContentTypeResponse), + ).rejects.toThrow( + new ScalewayError( + emptyContentTypeResponse.status, + `could not parse '' response`, + ), + ) }) - it(`returns a simple object for a valid 'Response' object`, async () => - expect(parseJson(makeValidJSONResponse())).resolves.toMatchObject( + it(`returns the response as text for unknown content type`, async () => + expect(parseAsIs(makeTextResponse('text-body'))).resolves.toBe('text-body')) + + it(`returns the proper object for a valid JSON object`, async () => + expect(parseJson(makeJSONResponse())).resolves.toMatchObject( SIMPLE_REQ_BODY, )) - it(`returns undefined for a 204 status code, even if content-type is json`, async () => + it(`returns the proper type for a Blob responseType`, async () => expect( - parseAsIs(makeValidJSONResponse(null, 204)), - ).resolves.toBeUndefined()) + parseBlob(makeTextResponse('hello world')).then(obj => typeof obj), + ).resolves.toBe('object')) + + it(`returns undefined for a 204 status code, even if content-type is json`, async () => + expect(parseAsIs(makeJSONResponse(null, 204))).resolves.toBeUndefined()) }) describe('fixLegacyTotalCount', () => { diff --git a/packages/clients/src/scw/fetch/build-fetcher.ts b/packages/clients/src/scw/fetch/build-fetcher.ts index ef6b25ef6..871b10252 100644 --- a/packages/clients/src/scw/fetch/build-fetcher.ts +++ b/packages/clients/src/scw/fetch/build-fetcher.ts @@ -78,6 +78,6 @@ export const buildFetcher = (settings: Settings, httpClient: typeof fetch) => { .then(prepareRequest(requestId)) .then(httpClient) .then(prepareResponse(requestId)) - .then(responseParser(unwrapper)) + .then(responseParser(unwrapper, request.responseType ?? 'json')) } } diff --git a/packages/clients/src/scw/fetch/response-parser.ts b/packages/clients/src/scw/fetch/response-parser.ts index f5a9563b1..7c93dd3f3 100644 --- a/packages/clients/src/scw/fetch/response-parser.ts +++ b/packages/clients/src/scw/fetch/response-parser.ts @@ -45,7 +45,10 @@ export const fixLegacyTotalCount = (obj: T, headers: Headers): T => { * @internal */ export const responseParser = - (unmarshaller: ResponseUnmarshaller) => + ( + unmarshaller: ResponseUnmarshaller, + responseType: 'json' | 'text' | 'blob', + ) => async (response: Response): Promise => { if (!(response instanceof Response)) throw new TypeError('Invalid response object') @@ -54,14 +57,16 @@ export const responseParser = if (response.status === 204) return unmarshaller(undefined) const contentType = response.headers.get('Content-Type') try { - switch (contentType) { - case 'application/json': - return unmarshaller( - fixLegacyTotalCount(await response.json(), response.headers), - ) - default: - return unmarshaller(await response.text()) + if (responseType === 'json' && contentType === 'application/json') { + return unmarshaller( + fixLegacyTotalCount(await response.json(), response.headers), + ) } + if (responseType === 'blob') { + return unmarshaller(await response.blob()) + } + + return unmarshaller(await response.text()) } catch (err) { throw new ScalewayError( response.status, diff --git a/packages/clients/src/scw/fetch/types.ts b/packages/clients/src/scw/fetch/types.ts index 7e7187dd4..e92860425 100644 --- a/packages/clients/src/scw/fetch/types.ts +++ b/packages/clients/src/scw/fetch/types.ts @@ -5,6 +5,7 @@ export type ScwRequest = { headers?: Record body?: string urlParams?: URLSearchParams + responseType?: 'json' | 'text' | 'blob' } /**