UNPKG

@tsed/schema

Version:
217 lines (216 loc) 7.22 kB
import { __decorate, __metadata } from "tslib"; import { DecoratorTypes, deepMerge, descriptorOf, isFunction, prototypeOf, Store } from "@tsed/core"; import { JsonEntityComponent } from "../decorators/config/jsonEntityComponent.js"; import { isSuccessStatus } from "../utils/isSuccessStatus.js"; import { JsonEntityStore } from "./JsonEntityStore.js"; import { JsonOperation } from "./JsonOperation.js"; import { JsonSchema } from "./JsonSchema.js"; /** * Store for method metadata, operations, and parameter information. * * JsonMethodStore manages metadata for controller methods decorated with operation * decorators like `@Get()`, `@Post()`, `@Returns()`, etc. It maintains the JsonOperation * for OpenAPI generation and coordinates method parameters. * * ### Responsibilities * * - **Operation Management**: Maintains JsonOperation for OpenAPI spec generation * - **Parameter Coordination**: Manages child JsonParameterStore instances * - **Middleware Configuration**: Handles before/after/use middleware registration * - **Response Configuration**: Manages return types and status codes * - **Route Information**: Stores HTTP methods, paths, and route metadata * * ### Usage * * ```typescript * // Get method store * const methodStore = JsonEntityStore.from(MyController, "myMethod"); * * // Access operation for OpenAPI * const operation = methodStore.operation; * * // Get parameters * const params = methodStore.parameters; * * // Check response type * const returnType = methodStore.type; * ``` * * ### Operation Structure * * Each method store contains a JsonOperation that includes: * - HTTP method and path information * - Request parameters (path, query, body, headers) * - Response definitions by status code * - Security requirements * - Tags and metadata * * ### Parameter Management * * The store maintains parameter metadata: * - Parameters stored in `children` map by index * - Accessible via `parameters` or `params` getters * - Each parameter has its own JsonParameterStore * * ### Middleware Integration * * Supports three middleware phases: * - **before**: Execute before the method * - **use**: Execute as main middleware * - **after**: Execute after the method * * @public */ let JsonMethodStore = class JsonMethodStore extends JsonEntityStore { constructor(options) { super({ store: Store.fromMethod(options.target, options.propertyKey), descriptor: descriptorOf(options.target, options.propertyKey), ...options }); this.parent = JsonEntityStore.from(this.target); this.middlewares = []; this.beforeMiddlewares = []; this.afterMiddlewares = []; /** * Ref to JsonOperation when the decorated object is a method. */ this.operation = new JsonOperation(); /** * List of children JsonEntityStore (properties or methods or params) */ this.children = new Map(); const { beforeMiddlewares = [], middlewares = [], afterMiddlewares = [] } = options; this.after(afterMiddlewares); this.before(beforeMiddlewares); this.use(middlewares); } get params() { return this.parameters; } get view() { return this.store.get("view"); } set view(view) { this.store.set("view", view); } get acceptMimes() { return this.store.get("acceptMimes", []); } set acceptMimes(mimes) { this.store.set("acceptMimes", mimes); } get parameters() { return [...this.children.values()]; } get operationPaths() { return this.operation.operationPaths; } get collectionType() { return this.schema.getTarget(); } set collectionType(type) { console.trace("collectionType is deprecated, use schema.collectionClass instead"); } get isCollection() { return this.schema.isCollection; } get schema() { if (this._schema) { return this._schema; } const responses = this.operation.getResponses(); const [, response] = [...responses.entries()].find(([key, response]) => { return isSuccessStatus(key); }) || []; if (response) { const firstMediaType = response.getContent().values().next().value; if (firstMediaType) { this._schema = firstMediaType.schema(); } } else { this._schema = new JsonSchema(); } return this._schema; } /** * Get an endpoint. * @param target * @param propertyKey * @param descriptor */ static get(target, propertyKey, descriptor) { descriptor = descriptor || descriptorOf(prototypeOf(target), propertyKey); return JsonEntityStore.from(prototypeOf(target), propertyKey, descriptor); } /** * TODO must be located on JsonOperation level directly * @param status * @param contentType * @param includes */ getResponseOptions(status, { contentType = "application/json", includes } = {}) { const media = this.operation.getResponseOf(status).getMedia(contentType, false); if (media && media.has("schema")) { const allowedGroups = media.schema().getAllowedGroups(); let groups = media.schema().getGroups(); if (includes && allowedGroups?.size) { groups = [...(groups || []), ...includes.filter((include) => allowedGroups.has(include))]; } return { type: media.schema().collectionClass, groups }; } return { type: this.type || Object }; } /** * Append middlewares to the beforeMiddlewares list. * @param args * @returns {EndpointMetadata} */ before(args) { this.beforeMiddlewares = this.beforeMiddlewares.concat(args).filter(isFunction); return this; } /** * Append middlewares to the afterMiddlewares list. * @param args * @returns {EndpointMetadata} */ after(args) { this.afterMiddlewares = this.afterMiddlewares.concat(args).filter(isFunction); return this; } /** * Store all arguments collected via Annotation. * @param args */ use(args) { this.middlewares = this.middlewares.concat(args).filter(isFunction); return this; } /** * Find the value at the controller level. Let this value be extended or overridden by the endpoint itself. * * @param key * @returns {any} */ get(key) { const ctrlValue = Store.from(this.target).get(key); return deepMerge(ctrlValue, this.store.get(key)); } getParamTypes() { return [...this.children.values()].reduce((obj, item) => ({ ...obj, [item.paramType]: true }), {}); } build() { this.parent.children.set(this.propertyName, this); } }; JsonMethodStore = __decorate([ JsonEntityComponent(DecoratorTypes.METHOD), __metadata("design:paramtypes", [Object]) ], JsonMethodStore); export { JsonMethodStore }; export const EndpointMetadata = JsonMethodStore;