From 59d324071cd6753bcd835b9d910b665a79cf1817 Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Mon, 15 Jul 2024 13:48:15 +0300 Subject: [PATCH 1/6] feat: allow form data through default body serializer --- packages/openapi-fetch/src/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/openapi-fetch/src/index.js b/packages/openapi-fetch/src/index.js index cdcc3052e..479657c3b 100644 --- a/packages/openapi-fetch/src/index.js +++ b/packages/openapi-fetch/src/index.js @@ -434,6 +434,9 @@ export function defaultPathSerializer(pathname, pathParams) { * @type {import("./index.js").defaultBodySerializer} */ export function defaultBodySerializer(body) { + if (body instanceof FormData) { + return body + } return JSON.stringify(body); } From 2d3418bdbd82b094bb7cc37bf35940802a0eacec Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Thu, 18 Jul 2024 21:45:37 +0300 Subject: [PATCH 2/6] chore: lint --- packages/openapi-fetch/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/openapi-fetch/src/index.js b/packages/openapi-fetch/src/index.js index 479657c3b..e878ef0b8 100644 --- a/packages/openapi-fetch/src/index.js +++ b/packages/openapi-fetch/src/index.js @@ -435,7 +435,7 @@ export function defaultPathSerializer(pathname, pathParams) { */ export function defaultBodySerializer(body) { if (body instanceof FormData) { - return body + return body; } return JSON.stringify(body); } From 2a964761de68917971f8b4176b8606e0126f834f Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Thu, 18 Jul 2024 21:47:15 +0300 Subject: [PATCH 3/6] test: send multipart/form-data with file --- packages/openapi-fetch/test/fixtures/api.d.ts | 40 +++++++++++++++++++ packages/openapi-fetch/test/fixtures/api.yaml | 20 ++++++++++ packages/openapi-fetch/test/index.test.ts | 27 +++++++++++++ 3 files changed, 87 insertions(+) diff --git a/packages/openapi-fetch/test/fixtures/api.d.ts b/packages/openapi-fetch/test/fixtures/api.d.ts index cad5160d4..b1675777c 100644 --- a/packages/openapi-fetch/test/fixtures/api.d.ts +++ b/packages/openapi-fetch/test/fixtures/api.d.ts @@ -814,6 +814,46 @@ export interface paths { patch?: never; trace?: never; }; + "/multipart-form-data-file-upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "multipart/form-data": string; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + text: string; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { diff --git a/packages/openapi-fetch/test/fixtures/api.yaml b/packages/openapi-fetch/test/fixtures/api.yaml index 8994e1ba8..011fcc768 100644 --- a/packages/openapi-fetch/test/fixtures/api.yaml +++ b/packages/openapi-fetch/test/fixtures/api.yaml @@ -470,6 +470,26 @@ paths: responses: 200: $ref: "#/components/responses/MultipleResponse" + /multipart-form-data-file-upload: + post: + requestBody: + content: + multipart/form-data: + schema: + type: string + format: binary + required: true + responses: + "200": + content: + application/json: + schema: + type: object + properties: + text: + type: string + required: + - text components: schemas: Post: diff --git a/packages/openapi-fetch/test/index.test.ts b/packages/openapi-fetch/test/index.test.ts index 687bdc957..14ee781fc 100644 --- a/packages/openapi-fetch/test/index.test.ts +++ b/packages/openapi-fetch/test/index.test.ts @@ -1352,6 +1352,33 @@ describe("client", () => { customProperty: "value", }); }); + + it('multipart/form-data with a file', async () => { + const TEST_STRING = 'Hello this is text file string'; + + const file = new Blob([TEST_STRING], { type: 'text/plain' }); + const formData = new FormData(); + formData.append('file', file); + + const client = createClient({ baseUrl }); + useMockRequestHandler({ + baseUrl, + method: "post", + path: "/multipart-form-data-file-upload", + handler: async (data) =>{ + // Get text from file and send it back + const formData = await data.request.formData(); + const text = await (formData.get('file') as File).text() + return new HttpResponse(JSON.stringify({text})) + }, + }); + const {data} = await client.POST("/multipart-form-data-file-upload", { + // @ts-ignore // TODO: how to get this to accept FormData? + body: formData, + }) + + expect(data?.text).toBe(TEST_STRING) + }) }); describe("responses", () => { From f9b5657698fd4a180c64a135d2dbd93ed6f34262 Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Thu, 18 Jul 2024 21:57:14 +0300 Subject: [PATCH 4/6] docs: openapi-fetch changeset patch --- .changeset/warm-plums-wait.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/warm-plums-wait.md diff --git a/.changeset/warm-plums-wait.md b/.changeset/warm-plums-wait.md new file mode 100644 index 000000000..00361e0e8 --- /dev/null +++ b/.changeset/warm-plums-wait.md @@ -0,0 +1,5 @@ +--- +"openapi-fetch": patch +--- + +Allow FormData through defaultBodySerializer From 724cd6742618799138af1bea4b1262d953031330 Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Thu, 18 Jul 2024 22:03:05 +0300 Subject: [PATCH 5/6] test: cast FormData to string instead of ts-ignore --- packages/openapi-fetch/test/index.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/openapi-fetch/test/index.test.ts b/packages/openapi-fetch/test/index.test.ts index 14ee781fc..4c6286290 100644 --- a/packages/openapi-fetch/test/index.test.ts +++ b/packages/openapi-fetch/test/index.test.ts @@ -1355,7 +1355,7 @@ describe("client", () => { it('multipart/form-data with a file', async () => { const TEST_STRING = 'Hello this is text file string'; - + const file = new Blob([TEST_STRING], { type: 'text/plain' }); const formData = new FormData(); formData.append('file', file); @@ -1373,8 +1373,8 @@ describe("client", () => { }, }); const {data} = await client.POST("/multipart-form-data-file-upload", { - // @ts-ignore // TODO: how to get this to accept FormData? - body: formData, + // TODO: how to get this to accept FormData? + body: formData as unknown as string, }) expect(data?.text).toBe(TEST_STRING) From 0abd93c1888237a828b897e139fee14b63f1ad92 Mon Sep 17 00:00:00 2001 From: thatsprettyfaroutman Date: Thu, 18 Jul 2024 22:04:51 +0300 Subject: [PATCH 6/6] chore: lint --- packages/openapi-fetch/test/index.test.ts | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/openapi-fetch/test/index.test.ts b/packages/openapi-fetch/test/index.test.ts index 4c6286290..0055dcba7 100644 --- a/packages/openapi-fetch/test/index.test.ts +++ b/packages/openapi-fetch/test/index.test.ts @@ -1353,32 +1353,32 @@ describe("client", () => { }); }); - it('multipart/form-data with a file', async () => { - const TEST_STRING = 'Hello this is text file string'; + it("multipart/form-data with a file", async () => { + const TEST_STRING = "Hello this is text file string"; - const file = new Blob([TEST_STRING], { type: 'text/plain' }); + const file = new Blob([TEST_STRING], { type: "text/plain" }); const formData = new FormData(); - formData.append('file', file); + formData.append("file", file); const client = createClient({ baseUrl }); - useMockRequestHandler({ + useMockRequestHandler({ baseUrl, method: "post", path: "/multipart-form-data-file-upload", - handler: async (data) =>{ + handler: async (data) => { // Get text from file and send it back const formData = await data.request.formData(); - const text = await (formData.get('file') as File).text() - return new HttpResponse(JSON.stringify({text})) + const text = await (formData.get("file") as File).text(); + return new HttpResponse(JSON.stringify({ text })); }, }); - const {data} = await client.POST("/multipart-form-data-file-upload", { - // TODO: how to get this to accept FormData? + const { data } = await client.POST("/multipart-form-data-file-upload", { + // TODO: how to get this to accept FormData? body: formData as unknown as string, - }) + }); - expect(data?.text).toBe(TEST_STRING) - }) + expect(data?.text).toBe(TEST_STRING); + }); }); describe("responses", () => {