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 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", () => {