Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions packages/clients/src/scw/fetch/__tests__/response-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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(<T>(response: unknown) => response as T)
const parseJson = responseParser(unmarshalJSON, 'json')
const parseAsIs = responseParser(
<T>(response: unknown) => response as T,
'json',
)
const parseBlob = responseParser(
<T>(response: unknown) => response as T,
'blob',
)

it(`triggers a type error for non 'Response' object`, () =>
expect(
Expand Down Expand Up @@ -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,
Expand All @@ -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', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/clients/src/scw/fetch/build-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ export const buildFetcher = (settings: Settings, httpClient: typeof fetch) => {
.then(prepareRequest(requestId))
.then(httpClient)
.then(prepareResponse(requestId))
.then(responseParser<T>(unwrapper))
.then(responseParser<T>(unwrapper, request.responseType ?? 'json'))
}
}
21 changes: 13 additions & 8 deletions packages/clients/src/scw/fetch/response-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export const fixLegacyTotalCount = <T>(obj: T, headers: Headers): T => {
* @internal
*/
export const responseParser =
<T>(unmarshaller: ResponseUnmarshaller<T>) =>
<T>(
unmarshaller: ResponseUnmarshaller<T>,
responseType: 'json' | 'text' | 'blob',
) =>
async (response: Response): Promise<T> => {
if (!(response instanceof Response))
throw new TypeError('Invalid response object')
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions packages/clients/src/scw/fetch/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type ScwRequest = {
headers?: Record<string, string>
body?: string
urlParams?: URLSearchParams
responseType?: 'json' | 'text' | 'blob'
}

/**
Expand Down