diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9471f0e6..0b1c212329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## Version 25 +### v25.1.0 + +- Ability to disable the depiction of the `HEAD` method: + - New option `hasHeadMethod` on the argument of `Documentation` and `Integration` constructors; + - Depicts the HEAD method for each Endpoint supporting the GET method; + - The option is enabled by default (the behaviour introduced in [v24.7.0](#v2470)); + - The feature suggested by [@GreaterTamarack](https://github.com/GreaterTamarack). + ### v25.0.0 - Supported Node.js versions: `^20.19.0 || ^22.12.0 || ^24.0.0`; diff --git a/README.md b/README.md index daf9af3275..59bef7ea17 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Therefore, many basic tasks can be accomplished faster and easier, in particular These people contributed to the improvement of the framework by reporting bugs, making changes and suggesting ideas: +[@GreaterTamarack](https://github.com/GreaterTamarack) [@pepegc](https://github.com/pepegc) [@MichaelHindley](https://github.com/MichaelHindley) [@zoton2](https://github.com/zoton2) diff --git a/express-zod-api/src/documentation.ts b/express-zod-api/src/documentation.ts index fa395d19bb..987f82acc8 100644 --- a/express-zod-api/src/documentation.ts +++ b/express-zod-api/src/documentation.ts @@ -59,6 +59,11 @@ interface DocumentationParams { descriptions?: Partial>; /** @default true */ hasSummaryFromDescription?: boolean; + /** + * @desc Depict the HEAD method for each Endpoint supporting the GET method (feature of Express) + * @default true + * */ + hasHeadMethod?: boolean; /** @default inline */ composition?: "inline" | "components"; /** @@ -149,6 +154,7 @@ export class Documentation extends OpenApiBuilder { tags, isHeader, hasSummaryFromDescription = true, + hasHeadMethod = true, composition = "inline", }: DocumentationParams) { super(); @@ -256,7 +262,7 @@ export class Documentation extends OpenApiBuilder { }; walkRouting({ routing, - onEndpoint: withHead(onEndpoint), + onEndpoint: hasHeadMethod ? withHead(onEndpoint) : onEndpoint, }); if (tags) this.rootDoc.tags = depictTags(tags); } diff --git a/express-zod-api/src/integration.ts b/express-zod-api/src/integration.ts index 2447bde556..6baa5e7adf 100644 --- a/express-zod-api/src/integration.ts +++ b/express-zod-api/src/integration.ts @@ -47,6 +47,11 @@ interface IntegrationParams { * @default z.undefined() * */ noContent?: z.ZodType; + /** + * @desc Depict the HEAD method for each Endpoint supporting the GET method (feature of Express) + * @default true + * */ + hasHeadMethod?: boolean; /** * @desc Handling rules for your own branded schemas. * @desc Keys: brands (recommended to use unique symbols). @@ -90,6 +95,7 @@ export class Integration extends IntegrationBase { subscriptionClassName = "Subscription", serverUrl = "https://example.com", noContent = z.undefined(), + hasHeadMethod = true, }: IntegrationParams) { super(serverUrl); const commons = { makeAlias: this.#makeAlias.bind(this) }; @@ -148,7 +154,7 @@ export class Integration extends IntegrationBase { }; walkRouting({ routing, - onEndpoint: withHead(onEndpoint), + onEndpoint: hasHeadMethod ? withHead(onEndpoint) : onEndpoint, }); this.#program.unshift(...this.#aliases.values()); this.#program.push( diff --git a/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap b/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap index ef43dbf30f..25ad042490 100644 --- a/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap @@ -789,40 +789,6 @@ paths: status: error error: message: Sample error message - head: - operationId: HeadV1GetSomething - parameters: - - name: array - in: query - required: true - description: HEAD /v1/getSomething Parameter - schema: - minItems: 1 - maxItems: 3 - type: array - items: - type: integer - exclusiveMinimum: 0 - maximum: 9007199254740991 - - name: unlimited - in: query - required: true - description: HEAD /v1/getSomething Parameter - schema: - type: array - items: - type: boolean - - name: transformer - in: query - required: true - description: HEAD /v1/getSomething Parameter - schema: - type: string - responses: - "200": - description: HEAD /v1/getSomething Positive response - "400": - description: HEAD /v1/getSomething Negative response components: schemas: {} responses: {} diff --git a/express-zod-api/tests/__snapshots__/integration.spec.ts.snap b/express-zod-api/tests/__snapshots__/integration.spec.ts.snap index 51918b3dac..fd3c5e9668 100644 --- a/express-zod-api/tests/__snapshots__/integration.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/integration.spec.ts.snap @@ -66,6 +66,161 @@ export type Request = keyof Input; " `; +exports[`Integration > Should support HEAD method by default 0 1`] = ` +"type SomeOf = T[keyof T]; + +/** get /v1/path */ +type GetV1PathInput = { + some: string; +}; + +/** get /v1/path */ +type GetV1PathPositiveVariant1 = { + status: "success"; + data: {}; +}; + +/** get /v1/path */ +interface GetV1PathPositiveResponseVariants { + 200: GetV1PathPositiveVariant1; +} + +/** get /v1/path */ +type GetV1PathNegativeVariant1 = { + status: "error"; + error: { + message: string; + }; +}; + +/** get /v1/path */ +interface GetV1PathNegativeResponseVariants { + 400: GetV1PathNegativeVariant1; +} + +/** head /v1/path */ +type HeadV1PathInput = { + some: string; +}; + +/** head /v1/path */ +type HeadV1PathPositiveVariant1 = undefined; + +/** head /v1/path */ +interface HeadV1PathPositiveResponseVariants { + 200: HeadV1PathPositiveVariant1; +} + +/** head /v1/path */ +type HeadV1PathNegativeVariant1 = undefined; + +/** head /v1/path */ +interface HeadV1PathNegativeResponseVariants { + 400: HeadV1PathNegativeVariant1; +} + +export type Path = "/v1/path"; + +export type Method = "get" | "post" | "put" | "delete" | "patch" | "head"; + +export interface Input { + "get /v1/path": GetV1PathInput; + "head /v1/path": HeadV1PathInput; +} + +export interface PositiveResponse { + "get /v1/path": SomeOf; + "head /v1/path": SomeOf; +} + +export interface NegativeResponse { + "get /v1/path": SomeOf; + "head /v1/path": SomeOf; +} + +export interface EncodedResponse { + "get /v1/path": GetV1PathPositiveResponseVariants & + GetV1PathNegativeResponseVariants; + "head /v1/path": HeadV1PathPositiveResponseVariants & + HeadV1PathNegativeResponseVariants; +} + +export interface Response { + "get /v1/path": + | PositiveResponse["get /v1/path"] + | NegativeResponse["get /v1/path"]; + "head /v1/path": + | PositiveResponse["head /v1/path"] + | NegativeResponse["head /v1/path"]; +} + +export type Request = keyof Input; +" +`; + +exports[`Integration > Should support HEAD method by default 1 1`] = ` +"type SomeOf = T[keyof T]; + +/** get /v1/path */ +type GetV1PathInput = { + some: string; +}; + +/** get /v1/path */ +type GetV1PathPositiveVariant1 = { + status: "success"; + data: {}; +}; + +/** get /v1/path */ +interface GetV1PathPositiveResponseVariants { + 200: GetV1PathPositiveVariant1; +} + +/** get /v1/path */ +type GetV1PathNegativeVariant1 = { + status: "error"; + error: { + message: string; + }; +}; + +/** get /v1/path */ +interface GetV1PathNegativeResponseVariants { + 400: GetV1PathNegativeVariant1; +} + +export type Path = "/v1/path"; + +export type Method = "get" | "post" | "put" | "delete" | "patch" | "head"; + +export interface Input { + "get /v1/path": GetV1PathInput; +} + +export interface PositiveResponse { + "get /v1/path": SomeOf; +} + +export interface NegativeResponse { + "get /v1/path": SomeOf; +} + +export interface EncodedResponse { + "get /v1/path": GetV1PathPositiveResponseVariants & + GetV1PathNegativeResponseVariants; +} + +export interface Response { + "get /v1/path": + | PositiveResponse["get /v1/path"] + | NegativeResponse["get /v1/path"]; +} + +export type Request = keyof Input; +" +`; + exports[`Integration > Should support multiple response schemas depending on status code 1`] = ` "type SomeOf = T[keyof T]; diff --git a/express-zod-api/tests/documentation.spec.ts b/express-zod-api/tests/documentation.spec.ts index 979afb038e..06b4e47f2a 100644 --- a/express-zod-api/tests/documentation.spec.ts +++ b/express-zod-api/tests/documentation.spec.ts @@ -52,6 +52,7 @@ describe("Documentation", () => { const literalValue = "something" as const; const spec = new Documentation({ config: sampleConfig, + hasHeadMethod: false, routing: { v1: { getSomething: defaultEndpointsFactory.build({ diff --git a/express-zod-api/tests/integration.spec.ts b/express-zod-api/tests/integration.spec.ts index 1a2bf304fc..eb44ed47c3 100644 --- a/express-zod-api/tests/integration.spec.ts +++ b/express-zod-api/tests/integration.spec.ts @@ -66,6 +66,25 @@ describe("Integration", () => { expect(await client.printFormatted()).toMatchSnapshot(); }); + test.each([undefined, false])( + "Should support HEAD method by default %#", + async (hasHeadMethod) => { + const client = new Integration({ + hasHeadMethod, + variant: "types", + routing: { + v1: { + "get path": defaultEndpointsFactory.buildVoid({ + input: z.object({ some: z.string() }), + handler: vi.fn(), + }), + }, + }, + }); + expect(await client.printFormatted()).toMatchSnapshot(); + }, + ); + test("Should support multiple response schemas depending on status code", async () => { const factory = new EndpointsFactory( new ResultHandler({