From 0566bd17faa8b90e0a7763125af2f577a6202660 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 26 Jul 2023 11:02:43 -0600 Subject: [PATCH 1/2] Fix 2XX, 4XX, 5XX responses --- .changeset/stale-shoes-sell.md | 5 ++++ packages/openapi-fetch/src/index.test.ts | 15 ++++++++++++ packages/openapi-fetch/src/index.ts | 4 ++-- packages/openapi-fetch/test/v1.d.ts | 23 ++++++++++++++++++ packages/openapi-fetch/test/v1.yaml | 30 ++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 .changeset/stale-shoes-sell.md diff --git a/.changeset/stale-shoes-sell.md b/.changeset/stale-shoes-sell.md new file mode 100644 index 000000000..d46f626e2 --- /dev/null +++ b/.changeset/stale-shoes-sell.md @@ -0,0 +1,5 @@ +--- +"openapi-fetch": patch +--- + +Fix 2XX, 4XX, 5XX responses diff --git a/packages/openapi-fetch/src/index.test.ts b/packages/openapi-fetch/src/index.test.ts index aff8ed55d..977f2c7a7 100644 --- a/packages/openapi-fetch/src/index.test.ts +++ b/packages/openapi-fetch/src/index.test.ts @@ -516,6 +516,21 @@ describe("client", () => { // assert array type (and only array type) was inferred expect(data.length).toBe(0); }); + + it("handles literal 2XX and 4XX codes", async () => { + const client = createClient(); + mockFetch({ status: 201, body: '{"status": "success"}' }); + const { data, error } = await client.PUT("/media", { body: { media: "base64", name: "myImage" } }); + + if (data) { + // assert 2XX type inferred correctly + expect(data.status).toBe("success"); + } else { + // assert 4XX type inferred correctly + // (this should be a dead code path but tests TS types) + expect(error.message).toBe("Error"); + } + }); }); describe("POST()", () => { diff --git a/packages/openapi-fetch/src/index.ts b/packages/openapi-fetch/src/index.ts index dc50902bf..b183763c4 100644 --- a/packages/openapi-fetch/src/index.ts +++ b/packages/openapi-fetch/src/index.ts @@ -29,9 +29,9 @@ export interface OperationObject { responses: any; } export type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"; -export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207; +export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX"; // prettier-ignore -export type ErrorStatus = 500 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 429 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | "default"; +export type ErrorStatus = 500 | '5XX' | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 429 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | '4XX' | "default"; // util /** Get a union of paths which have method */ diff --git a/packages/openapi-fetch/test/v1.d.ts b/packages/openapi-fetch/test/v1.d.ts index 664c8a40c..74b13f97d 100644 --- a/packages/openapi-fetch/test/v1.d.ts +++ b/packages/openapi-fetch/test/v1.d.ts @@ -112,6 +112,29 @@ export interface paths { "/header-params": { get: operations["getHeaderParams"]; }; + "/media": { + put: { + requestBody: { + content: { + "application/json": { + /** Format: blob */ + media: string; + name: string; + }; + }; + }; + responses: { + "2XX": { + content: { + "application/json": { + status: string; + }; + }; + }; + "4XX": components["responses"]["Error"]; + }; + }; + }; "/self": { get: { responses: { diff --git a/packages/openapi-fetch/test/v1.yaml b/packages/openapi-fetch/test/v1.yaml index ac5af8621..6425c1ac2 100644 --- a/packages/openapi-fetch/test/v1.yaml +++ b/packages/openapi-fetch/test/v1.yaml @@ -123,6 +123,36 @@ paths: - status 500: $ref: '#/components/responses/Error' + /media: + put: + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + media: + type: string + format: blob + name: + type: string + required: + - media + - name + responses: + 2XX: + content: + application/json: + schema: + type: object + properties: + status: + type: string + required: + - status + 4XX: + $ref: '#/components/responses/Error' /self: get: responses: From bd72890b5b7faae6fb04b340659875b148a50232 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 26 Jul 2023 11:13:51 -0600 Subject: [PATCH 2/2] Add 2XX, 4XX, 5XX test --- .../test/operation-object.test.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 packages/openapi-typescript/test/operation-object.test.ts diff --git a/packages/openapi-typescript/test/operation-object.test.ts b/packages/openapi-typescript/test/operation-object.test.ts new file mode 100644 index 000000000..a30addb18 --- /dev/null +++ b/packages/openapi-typescript/test/operation-object.test.ts @@ -0,0 +1,72 @@ +import type { GlobalContext, OperationObject } from "../src/types.js"; +import transformOperationObject from "../src/transform/operation-object.js"; + +const ctx: GlobalContext = { + additionalProperties: false, + alphabetize: false, + defaultNonNullable: false, + discriminators: {}, + emptyObjectsUnknown: false, + immutableTypes: false, + indentLv: 0, + operations: {}, + parameters: {}, + pathParamsAsTypes: false, + postTransform: undefined, + silent: true, + supportArrayLength: false, + transform: undefined, + excludeDeprecated: false, +}; + +describe("Operation Object", () => { + it("allows 2XX codes", () => { + const schema: OperationObject = { + responses: { + "2XX": { + description: "OK", + content: { + "application/json": { + schema: { type: "string" }, + }, + }, + }, + "4XX": { + description: "OK", + content: { + "application/json": { schema: { $ref: 'components["schemas"]["Error"]' } }, + }, + }, + "5XX": { + description: "OK", + content: { + "application/json": { schema: { $ref: 'components["schemas"]["Error"]' } }, + }, + }, + }, + }; + const generated = transformOperationObject(schema, { ctx, path: "#/paths/~get-item" }); + expect(generated).toBe(`{ + responses: { + /** @description OK */ + "2XX": { + content: { + "application/json": string; + }; + }; + /** @description OK */ + "4XX": { + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description OK */ + "5XX": { + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; +}`); + }); +});