Skip to content

Commit 1af5f7d

Browse files
committed
feat(code-gen): combine files & body handling when body is multipart
BREAKING CHANGE: - The open api importer is not compatible anymore with 'legacy' code-gen, please migrate to experimental code-gen. - Rewrite of FormData handling in the 'experimental' api clients, check the new generate output and retest your usages of features using multipart bodies / files. - In the near future, we will fully combine `files` & `body` handling for the backends as well.
1 parent a67b383 commit 1af5f7d

File tree

5 files changed

+124
-105
lines changed

5 files changed

+124
-105
lines changed

packages/code-gen/src/experimental/api-client/js-axios.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -208,35 +208,41 @@ export function jsAxiosGenerateFunction(
208208
)}(${args.join(", ")})`,
209209
);
210210

211-
if (route.files) {
211+
if (route.files || route.metadata?.requestBodyType === "form-data") {
212+
const parameter = route.body ? "body" : "files";
213+
212214
fileWrite(
213215
file,
214-
`
215-
const data = new FormData();
216-
217-
for (const key of Object.keys(files)) {
218-
const keyFiles = Array.isArray(files[key]) ? files[key] : [files[key]];
219-
220-
for (const file of keyFiles) {`,
216+
`const data = ${parameter} instanceof FormData ? ${parameter} : new FormData();`,
221217
);
222-
fileContextSetIndent(file, 2);
223218

224-
fileWrite(file, `data.append(key, file.data, file.name);`);
219+
fileBlockStart(file, `if (!(${parameter} instanceof FormData))`);
225220

226-
fileContextSetIndent(file, -2);
227-
fileWrite(file, `}\n}`);
228-
}
221+
/** @type {import("../generated/common/types.js").ExperimentalObjectDefinition} */
222+
// @ts-expect-error
223+
const type = structureResolveReference(
224+
generateContext.structure,
229225

230-
if (route.metadata?.requestBodyType === "form-data") {
231-
fileWrite(
232-
file,
233-
`const data = body instanceof FormData ? body : new FormData();`,
234-
);
235-
fileBlockStart(file, `if (!(body instanceof FormData))`);
236-
fileWrite(
237-
file,
238-
`for (const key of Object.keys(body)) { data.append(key, body[key]); }`,
226+
// @ts-expect-error
227+
route.body ?? route.files,
239228
);
229+
230+
for (const key of Object.keys(type.keys)) {
231+
const fieldType =
232+
type.keys[key].type === "reference"
233+
? structureResolveReference(generateContext.structure, type.keys[key])
234+
: type.keys[key];
235+
236+
if (fieldType.type === "file") {
237+
fileWrite(
238+
file,
239+
`data.append("${key}", ${parameter}["${key}"].data, ${parameter}["${key}"].name);`,
240+
);
241+
} else {
242+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
243+
}
244+
}
245+
240246
fileBlockEnd(file);
241247
}
242248

packages/code-gen/src/experimental/api-client/js-fetch.js

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -234,35 +234,40 @@ export function jsFetchGenerateFunction(
234234
)}(${args.join(", ")})`,
235235
);
236236

237-
if (route.files) {
237+
if (route.files || route.metadata?.requestBodyType === "form-data") {
238+
const parameter = route.body ? "body" : "files";
238239
fileWrite(
239240
file,
240-
`
241-
const data = new FormData();
242-
243-
for (const key of Object.keys(files)) {
244-
const keyFiles = Array.isArray(files[key]) ? files[key] : [files[key]];
245-
246-
for (const file of keyFiles) {`,
241+
`const data = ${parameter} instanceof FormData ? ${parameter} : new FormData();`,
247242
);
248-
fileContextSetIndent(file, 2);
249243

250-
fileWrite(file, `data.append(key, file.data, file.name);`);
244+
fileBlockStart(file, `if (!(${parameter} instanceof FormData))`);
251245

252-
fileContextSetIndent(file, -2);
253-
fileWrite(file, `}\n}`);
254-
}
246+
/** @type {import("../generated/common/types.js").ExperimentalObjectDefinition} */
247+
// @ts-expect-error
248+
const type = structureResolveReference(
249+
generateContext.structure,
255250

256-
if (route.metadata?.requestBodyType === "form-data") {
257-
fileWrite(
258-
file,
259-
`const data = body instanceof FormData ? body : new FormData();`,
260-
);
261-
fileBlockStart(file, `if (!(body instanceof FormData))`);
262-
fileWrite(
263-
file,
264-
`for (const key of Object.keys(body)) { data.append(key, body[key]); }`,
251+
// @ts-expect-error
252+
route.body ?? route.files,
265253
);
254+
255+
for (const key of Object.keys(type.keys)) {
256+
const fieldType =
257+
type.keys[key].type === "reference"
258+
? structureResolveReference(generateContext.structure, type.keys[key])
259+
: type.keys[key];
260+
261+
if (fieldType.type === "file") {
262+
fileWrite(
263+
file,
264+
`data.append("${key}", ${parameter}["${key}"].data, ${parameter}["${key}"].name);`,
265+
);
266+
} else {
267+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
268+
}
269+
}
270+
266271
fileBlockEnd(file);
267272
}
268273

packages/code-gen/src/experimental/api-client/ts-axios.js

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -166,39 +166,45 @@ export function tsAxiosGenerateFunction(
166166
)}(${args.join(", ")}): Promise<${contextNames.responseTypeName}>`,
167167
);
168168

169-
if (route.files) {
169+
if (route.files || route.metadata?.requestBodyType === "form-data") {
170+
const parameter = route.body ? "body" : "files";
171+
170172
fileWrite(
171173
file,
172-
`
173-
const data = new FormData();
174-
175-
for (const key of Object.keys(files)) {
176-
const keyFiles = Array.isArray((files as any)[key]) ? (files as any)[key] : [(files as any)[key]];
177-
178-
for (const file of keyFiles) {`,
174+
`const data = ${parameter} instanceof FormData ? ${parameter} : new FormData();`,
179175
);
180-
fileContextSetIndent(file, 2);
181176

182-
if (distilledTargetInfo.isReactNative) {
183-
fileWrite(file, `data.append(key, file);`);
184-
} else {
185-
fileWrite(file, `data.append(key, file.data, file.name);`);
186-
}
177+
fileBlockStart(file, `if (!(${parameter} instanceof FormData))`);
187178

188-
fileContextSetIndent(file, -2);
189-
fileWrite(file, `}\n}`);
190-
}
179+
/** @type {import("../generated/common/types.js").ExperimentalObjectDefinition} */
180+
// @ts-expect-error
181+
const type = structureResolveReference(
182+
generateContext.structure,
191183

192-
if (route.metadata?.requestBodyType === "form-data") {
193-
fileWrite(
194-
file,
195-
`const data = body instanceof FormData ? body : new FormData();`,
196-
);
197-
fileBlockStart(file, `if (!(body instanceof FormData))`);
198-
fileWrite(
199-
file,
200-
`for (const key of Object.keys(body)) { data.append(key, body[key]); }`,
184+
// @ts-expect-error
185+
route.body ?? route.files,
201186
);
187+
188+
for (const key of Object.keys(type.keys)) {
189+
const fieldType =
190+
type.keys[key].type === "reference"
191+
? structureResolveReference(generateContext.structure, type.keys[key])
192+
: type.keys[key];
193+
194+
if (fieldType.type === "file") {
195+
if (distilledTargetInfo.isReactNative) {
196+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
197+
} else {
198+
fileWrite(
199+
file,
200+
`data.append("${key}", ${parameter}["${key}"].data, ${parameter}["${key}"].name);`,
201+
);
202+
}
203+
} else {
204+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
205+
}
206+
}
207+
202208
fileBlockEnd(file);
203209
}
204210

@@ -221,16 +227,16 @@ for (const key of Object.keys(files)) {
221227

222228
if (route.files || route.metadata?.requestBodyType === "form-data") {
223229
fileWrite(file, `data,`);
230+
231+
if (distilledTargetInfo.isReactNative) {
232+
fileWrite(file, `headers: { "Content-Type": "multipart/form-data" },`);
233+
}
224234
}
225235

226236
if (route.body && route.metadata?.requestBodyType !== "form-data") {
227237
fileWrite(file, `data: body,`);
228238
}
229239

230-
if (distilledTargetInfo.isReactNative) {
231-
fileWrite(file, `headers: { "Content-Type": "multipart/form-data" },`);
232-
}
233-
234240
if (
235241
route.response &&
236242
structureResolveReference(generateContext.structure, route.response)

packages/code-gen/src/experimental/api-client/ts-fetch.js

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -226,39 +226,45 @@ export function tsFetchGenerateFunction(
226226
}>`,
227227
);
228228

229-
if (route.files) {
229+
if (route.files || route.metadata?.requestBodyType === "form-data") {
230+
const parameter = route.body ? "body" : "files";
231+
230232
fileWrite(
231233
file,
232-
`
233-
const data = new FormData();
234-
235-
for (const key of Object.keys(files)) {
236-
const keyFiles = Array.isArray((files as any)[key]) ? (files as any)[key] : [(files as any)[key]];
237-
238-
for (const file of keyFiles) {`,
234+
`const data = ${parameter} instanceof FormData ? ${parameter} : new FormData();`,
239235
);
240-
fileContextSetIndent(file, 2);
241236

242-
if (distilledTargetInfo.isReactNative) {
243-
fileWrite(file, `data.append(key, file);`);
244-
} else {
245-
fileWrite(file, `data.append(key, file.data, file.name);`);
246-
}
237+
fileBlockStart(file, `if (!(${parameter} instanceof FormData))`);
247238

248-
fileContextSetIndent(file, -2);
249-
fileWrite(file, `}\n}`);
250-
}
239+
/** @type {import("../generated/common/types.js").ExperimentalObjectDefinition} */
240+
// @ts-expect-error
241+
const type = structureResolveReference(
242+
generateContext.structure,
251243

252-
if (route.metadata?.requestBodyType === "form-data") {
253-
fileWrite(
254-
file,
255-
`const data = body instanceof FormData ? body : new FormData();`,
256-
);
257-
fileBlockStart(file, `if (!(body instanceof FormData))`);
258-
fileWrite(
259-
file,
260-
`for (const key of Object.keys(body)) { data.append(key, body[key]); }`,
244+
// @ts-expect-error
245+
route.body ?? route.files,
261246
);
247+
248+
for (const key of Object.keys(type.keys)) {
249+
const fieldType =
250+
type.keys[key].type === "reference"
251+
? structureResolveReference(generateContext.structure, type.keys[key])
252+
: type.keys[key];
253+
254+
if (fieldType.type === "file") {
255+
if (distilledTargetInfo.isReactNative) {
256+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
257+
} else {
258+
fileWrite(
259+
file,
260+
`data.append("${key}", ${parameter}["${key}"].data, ${parameter}["${key}"].name);`,
261+
);
262+
}
263+
} else {
264+
fileWrite(file, `data.append("${key}", ${parameter}["${key}"]);`);
265+
}
266+
}
267+
262268
fileBlockEnd(file);
263269
}
264270

packages/code-gen/src/open-api-importer.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,7 @@ function extractRoute(context, path, method) {
144144
compasStruct,
145145
);
146146

147-
if (body && JSON.stringify(body).includes(`type":"file"`)) {
148-
compasStruct.files = body;
149-
} else {
150-
compasStruct.body = body;
151-
}
147+
compasStruct.body = body;
152148

153149
compasStruct.response = transformResponse(
154150
context,

0 commit comments

Comments
 (0)