Skip to content

Commit f0c1ea9

Browse files
authored
fix: made fetchBodyOption handle binary format (#3804)
* fix: made fetchBodyOption handle binary format * fix: fixed type error * feat: added 'isBlob' to body * test: added test for body isBlob * fix: added isBlob false to the fallback * fix: added missing isBlob to tests * fix: fix further linting issues
1 parent 0a282ff commit f0c1ea9

11 files changed

Lines changed: 224 additions & 3 deletions

File tree

packages/angular/src/http-client.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ const createVerbOption = (
224224
contentType: '',
225225
formData: '',
226226
formUrlEncoded: '',
227+
isBlob: false,
227228
isOptional: true,
228229
},
229230
headers: undefined,
@@ -1018,6 +1019,7 @@ describe('angular HttpClient generator', () => {
10181019
contentType: 'application/json',
10191020
formData: '',
10201021
formUrlEncoded: '',
1022+
isBlob: false,
10211023
isOptional: false,
10221024
},
10231025
props: [
@@ -1059,6 +1061,7 @@ describe('angular HttpClient generator', () => {
10591061
contentType: 'application/json',
10601062
formData: '',
10611063
formUrlEncoded: '',
1064+
isBlob: false,
10621065
isOptional: false,
10631066
},
10641067
props: [
@@ -1109,6 +1112,7 @@ describe('angular HttpClient generator', () => {
11091112
contentType: '',
11101113
formData: '',
11111114
formUrlEncoded: '',
1115+
isBlob: false,
11121116
isOptional: true,
11131117
},
11141118
response: baseResponse({
@@ -1182,6 +1186,7 @@ describe('angular HttpClient generator', () => {
11821186
contentType: 'application/json',
11831187
formData: '',
11841188
formUrlEncoded: '',
1189+
isBlob: false,
11851190
isOptional: true,
11861191
},
11871192
props: [
@@ -1310,6 +1315,7 @@ describe('angular HttpClient generator', () => {
13101315
contentType: 'application/json',
13111316
formData: '',
13121317
formUrlEncoded: '',
1318+
isBlob: false,
13131319
isOptional: false,
13141320
},
13151321
props: [
@@ -1372,6 +1378,7 @@ describe('angular HttpClient generator', () => {
13721378
contentType: 'application/json',
13731379
formData: '',
13741380
formUrlEncoded: '',
1381+
isBlob: false,
13751382
isOptional: true,
13761383
},
13771384
props: [
@@ -1457,6 +1464,7 @@ describe('angular HttpClient generator', () => {
14571464
contentType: 'application/json',
14581465
formData: '',
14591466
formUrlEncoded: '',
1467+
isBlob: false,
14601468
isOptional: false,
14611469
},
14621470
props: [
@@ -1576,6 +1584,7 @@ describe('angular HttpClient generator', () => {
15761584
contentType: 'application/json',
15771585
formData: '',
15781586
formUrlEncoded: '',
1587+
isBlob: false,
15791588
isOptional: false,
15801589
},
15811590
props: [
@@ -1866,6 +1875,7 @@ describe('angular HttpClient generator', () => {
18661875
contentType: 'application/json',
18671876
formData: '',
18681877
formUrlEncoded: '',
1878+
isBlob: false,
18691879
isOptional: false,
18701880
},
18711881
props: [

packages/angular/src/http-resource.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ const createVerbOption = (
270270
contentType: '',
271271
formData: '',
272272
formUrlEncoded: '',
273+
isBlob: false,
273274
isOptional: true,
274275
},
275276
headers: undefined,
@@ -623,6 +624,7 @@ describe('angular httpResource generator', () => {
623624
contentType: 'application/json',
624625
formData: '',
625626
formUrlEncoded: '',
627+
isBlob: false,
626628
isOptional: false,
627629
},
628630
props: [
@@ -678,6 +680,7 @@ describe('angular httpResource generator', () => {
678680
contentType: 'application/json',
679681
formData: '',
680682
formUrlEncoded: '',
683+
isBlob: false,
681684
isOptional: true,
682685
},
683686
props: [
@@ -731,6 +734,7 @@ describe('angular httpResource generator', () => {
731734
contentType: 'application/json',
732735
formData: '',
733736
formUrlEncoded: '',
737+
isBlob: false,
734738
isOptional: false,
735739
},
736740
props: [
@@ -780,6 +784,7 @@ describe('angular httpResource generator', () => {
780784
contentType: 'application/json',
781785
formData: '',
782786
formUrlEncoded: '',
787+
isBlob: false,
783788
isOptional: false,
784789
},
785790
props: [
@@ -861,6 +866,7 @@ describe('angular httpResource generator', () => {
861866
contentType: 'application/json',
862867
formData: '',
863868
formUrlEncoded: '',
869+
isBlob: false,
864870
isOptional: false,
865871
},
866872
props: [
@@ -955,6 +961,7 @@ describe('angular httpResource generator', () => {
955961
contentType: 'application/json',
956962
formData: '',
957963
formUrlEncoded: '',
964+
isBlob: false,
958965
isOptional: false,
959966
},
960967
props: [
@@ -1375,6 +1382,7 @@ describe('angular httpResource generator', () => {
13751382
formData: '',
13761383
formUrlEncoded: '',
13771384
isOptional: false,
1385+
isBlob: false,
13781386
},
13791387
params: [],
13801388
props: [
@@ -1978,6 +1986,7 @@ describe('angular httpResource generator', () => {
19781986
contentType: 'application/json',
19791987
formData: '',
19801988
formUrlEncoded: '',
1989+
isBlob: false,
19811990
isOptional: false,
19821991
},
19831992
response: baseResponse({
@@ -2262,6 +2271,7 @@ describe('angular httpResource generator', () => {
22622271
contentType: 'application/json',
22632272
formData: '',
22642273
formUrlEncoded: '',
2274+
isBlob: false,
22652275
isOptional: false,
22662276
},
22672277
props: [
@@ -2482,6 +2492,7 @@ describe('angular httpResource generator', () => {
24822492
contentType: 'application/json',
24832493
formData: '',
24842494
formUrlEncoded: '',
2495+
isBlob: false,
24852496
isOptional: false,
24862497
},
24872498
queryParams: createQueryParams({ isOptional: false }),
@@ -3171,6 +3182,7 @@ describe('angular httpResource generator', () => {
31713182
contentType: 'application/json',
31723183
formData: '',
31733184
formUrlEncoded: '',
3185+
isBlob: false,
31743186
isOptional: false,
31753187
},
31763188
props: [
@@ -3243,6 +3255,7 @@ describe('angular httpResource generator', () => {
32433255
contentType: 'application/json',
32443256
formData: '',
32453257
formUrlEncoded: '',
3258+
isBlob: false,
32463259
isOptional: false,
32473260
},
32483261
props: [

packages/angular/src/utils.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ const makeVerb = (operationId: string, tags: string[]): GeneratorVerbOptions =>
400400
formData: '',
401401
formUrlEncoded: '',
402402
isOptional: true,
403+
isBlob: false,
403404
},
404405
headers: undefined,
405406
queryParams: undefined,

packages/core/src/generators/options.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const minimalBody: GetterBody = {
4242
formData: undefined,
4343
formUrlEncoded: undefined,
4444
contentType: 'application/json',
45+
isBlob: false,
4546
isOptional: false,
4647
};
4748

packages/core/src/generators/verbs-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ export async function generateVerbOptions({
394394
formUrlEncoded: '',
395395
contentType: '',
396396
isOptional: false,
397+
isBlob: false,
397398
};
398399

399400
const verbOption = await buildVerbOption({

packages/core/src/getters/body.test.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,183 @@ describe('getBody', () => {
222222
expect(result.isOptional).toBe(true);
223223
});
224224

225+
describe('isBlob detection', () => {
226+
it.each([
227+
['application/octet-stream'],
228+
['application/pdf'],
229+
['application/zip'],
230+
])('sets isBlob to true for %s request body', (binaryApplicationType) => {
231+
const requestBody: OpenApiRequestBodyObject = {
232+
content: {
233+
[binaryApplicationType]: {
234+
schema: { type: 'string', format: 'binary' },
235+
},
236+
},
237+
required: true,
238+
};
239+
240+
const result = getBody({
241+
requestBody,
242+
operationName: 'uploadFile',
243+
context: createContext(),
244+
});
245+
246+
expect(result.isBlob).toBe(true);
247+
});
248+
249+
it('sets isBlob to false for application/json request body', () => {
250+
const requestBody: OpenApiRequestBodyObject = {
251+
content: {
252+
'application/json': {
253+
schema: {
254+
type: 'object',
255+
properties: { name: { type: 'string' } },
256+
},
257+
},
258+
},
259+
required: true,
260+
};
261+
262+
const result = getBody({
263+
requestBody,
264+
operationName: 'createPet',
265+
context: createContext(),
266+
});
267+
268+
expect(result.isBlob).toBe(false);
269+
});
270+
271+
it('sets isBlob to true for text/csv with format: binary schema', () => {
272+
const requestBody: OpenApiRequestBodyObject = {
273+
content: {
274+
'text/csv': {
275+
schema: { type: 'string', format: 'binary' },
276+
},
277+
},
278+
required: true,
279+
};
280+
281+
const result = getBody({
282+
requestBody,
283+
operationName: 'uploadCsv',
284+
context: createContext(),
285+
});
286+
287+
expect(result.isBlob).toBe(true);
288+
});
289+
290+
it('sets isBlob to true for image content type', () => {
291+
const requestBody: OpenApiRequestBodyObject = {
292+
content: {
293+
'image/png': {
294+
schema: { type: 'string', format: 'binary' },
295+
},
296+
},
297+
required: true,
298+
};
299+
300+
const result = getBody({
301+
requestBody,
302+
operationName: 'uploadImage',
303+
context: createContext(),
304+
});
305+
306+
expect(result.isBlob).toBe(true);
307+
});
308+
309+
it('sets isBlob to true for */* with inline format: binary schema', () => {
310+
const requestBody: OpenApiRequestBodyObject = {
311+
content: {
312+
'*/*': {
313+
schema: { type: 'string', format: 'binary' },
314+
},
315+
},
316+
required: true,
317+
};
318+
319+
const result = getBody({
320+
requestBody,
321+
operationName: 'uploadFile',
322+
context: createContext(),
323+
});
324+
325+
expect(result.isBlob).toBe(true);
326+
});
327+
328+
it('sets isBlob to true for $ref to a format: binary schema', () => {
329+
const context = createContext();
330+
context.spec.components = {
331+
...context.spec.components,
332+
schemas: {
333+
...context.spec.components?.schemas,
334+
TestPdfFile: { type: 'string', format: 'binary' },
335+
},
336+
};
337+
338+
const requestBody: OpenApiRequestBodyObject = {
339+
content: {
340+
'*/*': {
341+
schema: { $ref: '#/components/schemas/TestPdfFile' },
342+
},
343+
},
344+
required: true,
345+
};
346+
347+
const result = getBody({
348+
requestBody,
349+
operationName: 'uploadFile',
350+
context,
351+
});
352+
353+
expect(result.isBlob).toBe(true);
354+
});
355+
356+
it('sets isBlob to true for schema with contentMediaType: application/octet-stream', () => {
357+
const requestBody: OpenApiRequestBodyObject = {
358+
content: {
359+
'application/json': {
360+
schema: {
361+
type: 'string',
362+
contentMediaType: 'application/octet-stream',
363+
},
364+
},
365+
},
366+
required: true,
367+
};
368+
369+
const result = getBody({
370+
requestBody,
371+
operationName: 'uploadFile',
372+
context: createContext(),
373+
});
374+
375+
expect(result.isBlob).toBe(true);
376+
});
377+
378+
it('sets isBlob to false when contentMediaType has contentEncoding (base64-encoded string)', () => {
379+
const requestBody: OpenApiRequestBodyObject = {
380+
content: {
381+
'application/json': {
382+
schema: {
383+
type: 'string',
384+
contentMediaType: 'application/octet-stream',
385+
contentEncoding: 'base64',
386+
},
387+
},
388+
},
389+
required: true,
390+
};
391+
392+
const result = getBody({
393+
requestBody,
394+
operationName: 'uploadFile',
395+
context: createContext(),
396+
});
397+
398+
expect(result.isBlob).toBe(false);
399+
});
400+
});
401+
225402
describe('x-codegen-request-body-name', () => {
226403
it('uses custom name from inline request body extension', () => {
227404
const result = getBody({

0 commit comments

Comments
 (0)