From b86c8c31b331e2ec839fa27d3a039490fbf9f49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzegorzewski?= Date: Sun, 29 Jun 2025 17:45:06 +0200 Subject: [PATCH 1/2] fix(#2364): Add support for passing parameters to @ApiOperation This commit introduces the ability to pass parameters directly to the @ApiOperation decorator, allowing developers to define path parameters within the decorator itself. This simplifies OpenAPI schema generation and improves developer experience by reducing boilerplate code. Changes include: - Added `parameters` option to ApiOperationOptions type - Updated OperationMetadata to include parameters property - Modified @ApiOperation decorator to accept and store parameter metadata - Added tests to verify parameter handling This change aligns with common usage patterns for OpenAPI specifications and provides a more intuitive way to define API operations with required parameters. #closes #2364 --- .../src/decorators/api-operation.ts | 9 +++++- .../src/metadata/operation.ts | 8 +++++- .../openapi-metadata/test/decorators.test.ts | 28 +++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/openapi-metadata/src/decorators/api-operation.ts b/packages/openapi-metadata/src/decorators/api-operation.ts index 99d210fc8..58995aa14 100644 --- a/packages/openapi-metadata/src/decorators/api-operation.ts +++ b/packages/openapi-metadata/src/decorators/api-operation.ts @@ -1,4 +1,8 @@ -import { type OperationMetadata, OperationMetadataStorage } from "../metadata/operation.js"; +import { + type OperationMetadata, + OperationMetadataStorage, + OperationParameterMetadataStorage, +} from "../metadata/index.js"; export type ApiOperationOptions = OperationMetadata; @@ -11,5 +15,8 @@ export type ApiOperationOptions = OperationMetadata; export function ApiOperation(options: ApiOperationOptions): MethodDecorator { return (target, propertyKey) => { OperationMetadataStorage.defineMetadata(target, options, propertyKey); + if (Array.isArray(options.parameters)) { + OperationParameterMetadataStorage.mergeMetadata(target, options.parameters, propertyKey); + } }; } diff --git a/packages/openapi-metadata/src/metadata/operation.ts b/packages/openapi-metadata/src/metadata/operation.ts index a2aafe188..4363a41b8 100644 --- a/packages/openapi-metadata/src/metadata/operation.ts +++ b/packages/openapi-metadata/src/metadata/operation.ts @@ -1,8 +1,9 @@ import type { OpenAPIV3 } from "openapi-types"; import type { HttpMethods } from "../types.js"; import { createMetadataStorage } from "./factory.js"; +import type { OperationParameterMetadata } from "./operation-parameter.js"; -export type OperationMetadata = Omit & { +export type OperationMetadata = Omit & { /** * Operation path. * Can include parameters. @@ -13,6 +14,11 @@ export type OperationMetadata = Omit & { * Available methods for this operation. */ methods?: HttpMethods[]; + + /** + * Represents metadata about an operation parameter. + */ + parameters?: OperationParameterMetadata[]; }; export const OperationMetadataKey = Symbol("Operation"); diff --git a/packages/openapi-metadata/test/decorators.test.ts b/packages/openapi-metadata/test/decorators.test.ts index b193be263..87633bb82 100644 --- a/packages/openapi-metadata/test/decorators.test.ts +++ b/packages/openapi-metadata/test/decorators.test.ts @@ -1,11 +1,15 @@ import "reflect-metadata"; import { + ApiBasicAuth, + ApiBearerAuth, ApiBody, ApiCookie, + ApiCookieAuth, ApiExcludeController, ApiExcludeOperation, ApiExtraModels, ApiHeader, + ApiOauth2, ApiOperation, ApiParam, ApiProperty, @@ -19,26 +23,40 @@ import { ExtraModelsMetadataStorage, OperationBodyMetadataStorage, OperationMetadataStorage, + type OperationParameterMetadata, OperationParameterMetadataStorage, OperationResponseMetadataStorage, OperationSecurityMetadataStorage, PropertyMetadataStorage, } from "../src/metadata/index.js"; -import { ApiBasicAuth, ApiBearerAuth, ApiCookieAuth, ApiOauth2 } from "../src/decorators/api-security.js"; test("@ApiOperation", () => { + const parameters: OperationParameterMetadata[] = [ + { + in: "path", + name: "id", + }, + ] as const; + class MyController { - @ApiOperation({ summary: "Hello", path: "/test", methods: ["get"] }) + @ApiOperation({ + summary: "Hello", + path: "/test", + methods: ["get"], + parameters, + }) operation() {} } - const metadata = OperationMetadataStorage.getMetadata(MyController.prototype, "operation"); - - expect(metadata).toEqual({ + const operationMetadata = OperationMetadataStorage.getMetadata(MyController.prototype, "operation"); + const parameterMetadata = OperationParameterMetadataStorage.getMetadata(MyController.prototype, "operation"); + expect(operationMetadata).toEqual({ summary: "Hello", path: "/test", methods: ["get"], + parameters, }); + expect(parameterMetadata).toEqual(parameters); }); test("@ApiBody", () => { From 3539d830729cbd4ade3ee2327753a6003466d5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzegorzewski?= Date: Sun, 29 Jun 2025 17:52:36 +0200 Subject: [PATCH 2/2] fix(#2364): Allow adding params via ApiOperation tag This commit fixes issue #2364 by enabling developers to specify parameters directly within the @ApiOperation decorator. This streamlines OpenAPI schema generation and improves developer experience with a more concise syntax for defining API operations that require specific input parameters. --- .changeset/itchy-jobs-create.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/itchy-jobs-create.md diff --git a/.changeset/itchy-jobs-create.md b/.changeset/itchy-jobs-create.md new file mode 100644 index 000000000..2b9fe311c --- /dev/null +++ b/.changeset/itchy-jobs-create.md @@ -0,0 +1,5 @@ +--- +"openapi-metadata": patch +--- + +fix(#2364) for allowing to add params via ApiOperation tag