Skip to content

Commit 1b4bf75

Browse files
QuiiBzVinzius
andauthored
feat: allow Blob response thanks to responseType parameter (#163)
Co-authored-by: Vincent Germain <[email protected]>
1 parent d14cdd1 commit 1b4bf75

File tree

4 files changed

+59
-29
lines changed

4 files changed

+59
-29
lines changed

packages/clients/src/scw/fetch/__tests__/response-parser.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { fixLegacyTotalCount, responseParser } from '../response-parser'
66

77
const SIMPLE_REQ_BODY = { 'what-is-life': 42 }
88

9-
const convertObjToBuffer = (obj: JSON): Buffer => {
9+
const convertObjToBuffer = (obj: unknown): Buffer => {
1010
const str = JSON.stringify(obj)
1111
const bytes = new TextEncoder().encode(str)
1212

@@ -19,24 +19,35 @@ const unmarshalJSON = (obj: unknown) => {
1919
return obj
2020
}
2121

22-
const makeValidJSONResponse = (
23-
value: JSON | null = SIMPLE_REQ_BODY,
22+
const makeResponse = (
23+
value: unknown,
2424
status = 200,
25+
contentType: string | undefined = undefined,
2526
) =>
2627
new Response(value !== null ? convertObjToBuffer(value) : value, {
27-
headers: { 'Content-Type': 'application/json' },
28+
headers: contentType ? { 'Content-Type': contentType } : undefined,
2829
status,
2930
})
3031

31-
const makeValidTextResponse = (value: string | null, status = 200) =>
32+
const makeJSONResponse = (value: JSON = SIMPLE_REQ_BODY, status = 200) =>
33+
makeResponse(value, status, 'application/json')
34+
35+
const makeTextResponse = (value: string, status = 200) =>
3236
new Response(value, {
3337
headers: { 'Content-Type': 'plain/text' },
3438
status,
3539
})
3640

3741
describe(`responseParser`, () => {
38-
const parseJson = responseParser(unmarshalJSON)
39-
const parseAsIs = responseParser(<T>(response: unknown) => response as T)
42+
const parseJson = responseParser(unmarshalJSON, 'json')
43+
const parseAsIs = responseParser(
44+
<T>(response: unknown) => response as T,
45+
'json',
46+
)
47+
const parseBlob = responseParser(
48+
<T>(response: unknown) => response as T,
49+
'blob',
50+
)
4051

4152
it(`triggers a type error for non 'Response' object`, () =>
4253
expect(
@@ -87,12 +98,13 @@ describe(`responseParser`, () => {
8798
})
8899

89100
it(`triggers an error for unsuccessful unmarshalling`, async () => {
90-
const validResponse = makeValidJSONResponse()
101+
const validResponse = makeJSONResponse()
102+
const emptyContentTypeResponse = makeResponse('some-text', 200, '')
91103

92104
await expect(
93105
responseParser(() => {
94106
throw new Error(`couldn't unwrap response value`)
95-
})(validResponse.clone()),
107+
}, 'json')(validResponse.clone()),
96108
).rejects.toThrow(
97109
new ScalewayError(
98110
validResponse.status,
@@ -104,30 +116,42 @@ describe(`responseParser`, () => {
104116
responseParser(() => {
105117
// eslint-disable-next-line @typescript-eslint/no-throw-literal
106118
throw 'not-of-error-type'
107-
})(validResponse.clone()),
119+
}, 'text')(validResponse.clone()),
108120
).rejects.toThrow(
109121
new ScalewayError(
110122
validResponse.status,
111123
`could not parse 'application/json' response`,
112124
),
113125
)
114-
})
115126

116-
it(`returns the response as-if for unknown content type`, async () => {
117-
const textResponse = makeValidTextResponse('text-body')
118-
119-
return expect(parseAsIs(textResponse)).resolves.toBe('text-body')
127+
await expect(
128+
responseParser(() => {
129+
// eslint-disable-next-line @typescript-eslint/no-throw-literal
130+
throw 'not-of-error-type'
131+
}, 'blob')(emptyContentTypeResponse),
132+
).rejects.toThrow(
133+
new ScalewayError(
134+
emptyContentTypeResponse.status,
135+
`could not parse '' response`,
136+
),
137+
)
120138
})
121139

122-
it(`returns a simple object for a valid 'Response' object`, async () =>
123-
expect(parseJson(makeValidJSONResponse())).resolves.toMatchObject(
140+
it(`returns the response as text for unknown content type`, async () =>
141+
expect(parseAsIs(makeTextResponse('text-body'))).resolves.toBe('text-body'))
142+
143+
it(`returns the proper object for a valid JSON object`, async () =>
144+
expect(parseJson(makeJSONResponse())).resolves.toMatchObject(
124145
SIMPLE_REQ_BODY,
125146
))
126147

127-
it(`returns undefined for a 204 status code, even if content-type is json`, async () =>
148+
it(`returns the proper type for a Blob responseType`, async () =>
128149
expect(
129-
parseAsIs(makeValidJSONResponse(null, 204)),
130-
).resolves.toBeUndefined())
150+
parseBlob(makeTextResponse('hello world')).then(obj => typeof obj),
151+
).resolves.toBe('object'))
152+
153+
it(`returns undefined for a 204 status code, even if content-type is json`, async () =>
154+
expect(parseAsIs(makeJSONResponse(null, 204))).resolves.toBeUndefined())
131155
})
132156

133157
describe('fixLegacyTotalCount', () => {

packages/clients/src/scw/fetch/build-fetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,6 @@ export const buildFetcher = (settings: Settings, httpClient: typeof fetch) => {
7878
.then(prepareRequest(requestId))
7979
.then(httpClient)
8080
.then(prepareResponse(requestId))
81-
.then(responseParser<T>(unwrapper))
81+
.then(responseParser<T>(unwrapper, request.responseType ?? 'json'))
8282
}
8383
}

packages/clients/src/scw/fetch/response-parser.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ export const fixLegacyTotalCount = <T>(obj: T, headers: Headers): T => {
4545
* @internal
4646
*/
4747
export const responseParser =
48-
<T>(unmarshaller: ResponseUnmarshaller<T>) =>
48+
<T>(
49+
unmarshaller: ResponseUnmarshaller<T>,
50+
responseType: 'json' | 'text' | 'blob',
51+
) =>
4952
async (response: Response): Promise<T> => {
5053
if (!(response instanceof Response))
5154
throw new TypeError('Invalid response object')
@@ -54,14 +57,16 @@ export const responseParser =
5457
if (response.status === 204) return unmarshaller(undefined)
5558
const contentType = response.headers.get('Content-Type')
5659
try {
57-
switch (contentType) {
58-
case 'application/json':
59-
return unmarshaller(
60-
fixLegacyTotalCount(await response.json(), response.headers),
61-
)
62-
default:
63-
return unmarshaller(await response.text())
60+
if (responseType === 'json' && contentType === 'application/json') {
61+
return unmarshaller(
62+
fixLegacyTotalCount(await response.json(), response.headers),
63+
)
6464
}
65+
if (responseType === 'blob') {
66+
return unmarshaller(await response.blob())
67+
}
68+
69+
return unmarshaller(await response.text())
6570
} catch (err) {
6671
throw new ScalewayError(
6772
response.status,

packages/clients/src/scw/fetch/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type ScwRequest = {
55
headers?: Record<string, string>
66
body?: string
77
urlParams?: URLSearchParams
8+
responseType?: 'json' | 'text' | 'blob'
89
}
910

1011
/**

0 commit comments

Comments
 (0)