@tsed/schema
Version:
JsonSchema module for Ts.ED Framework
157 lines (156 loc) • 5.45 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { ancestorsOf, DecoratorTypes, isClass, isMethodDescriptor, Metadata, prototypeOf } from "@tsed/core";
import { JsonEntityComponent } from "../decorators/config/jsonEntityComponent.js";
import { JsonEntityStore } from "./JsonEntityStore.js";
import { JsonParameter } from "./JsonParameter.js";
/**
* Store for method parameter metadata and schema information.
*
* JsonParameterStore manages metadata for method parameters decorated with decorators
* like `@BodyParams()`, `@PathParams()`, `@QueryParams()`, etc. It handles parameter
* validation, transformation pipes, and OpenAPI parameter generation.
*
* ### Responsibilities
*
* - **Parameter Definition**: Maintains JsonParameter for OpenAPI spec generation
* - **Type Resolution**: Resolves parameter types from TypeScript metadata
* - **Validation**: Manages required/optional state and validation rules
* - **Transformation**: Coordinates pipe execution for value transformation
* - **Location Tracking**: Stores where the parameter comes from (path, query, body, etc.)
*
* ### Usage
*
* ```typescript
* // Get parameter store
* const paramStore = JsonParameterStore.get(MyController, "myMethod", 0);
*
* // Access parameter definition
* const parameter = paramStore.parameter;
*
* // Check if required
* const isRequired = paramStore.required;
*
* // Get all parameters for a method
* const params = JsonParameterStore.getParams(MyController, "myMethod");
* ```
*
* ### Parameter Types
*
* Parameters can come from different locations:
* - **path**: URL path parameters (`/users/:id`)
* - **query**: Query string parameters (`?page=1`)
* - **body**: Request body
* - **header**: HTTP headers
* - **cookies**: Cookie values
* - **files**: File uploads
*
* ### Validation
*
* The store provides validation helpers:
* - `required`: Marks parameter as required
* - `allowedRequiredValues`: Values that bypass required check
* - `isRequired(value)`: Checks if a value satisfies required constraint
*
* ### Transformation Pipes
*
* Parameters can have transformation pipes that process values:
* ```typescript
* @Controller("/")
* class MyController {
* @Get("/:id")
* get(@PathParams("id") @UsePipe(ParseIntPipe) id: number) {
* // id is automatically transformed to number
* }
* }
* ```
*
* ### Expression Binding
*
* Parameters can use expressions to extract nested values:
* ```typescript
* @BodyParams("user.address.city") city: string
* // Extracts: request.body.user.address.city
* ```
*
* @public
*/
let JsonParameterStore = class JsonParameterStore extends JsonEntityStore {
constructor(options) {
super(options);
/**
* Ref to JsonParameter when the decorated object is a parameter.
*/
this.parameter = new JsonParameter();
this.parent = JsonEntityStore.fromMethod(this.target, this.propertyKey);
this.pipes = options.pipes || [];
this.paramType = options.paramType || this.paramType;
this.expression = options.expression || this.expression;
this.dataPath = options.dataPath || this.dataPath;
}
/**
* Return the required state.
* @returns {boolean}
*/
get required() {
return !!this.parameter.get("required");
}
set required(value) {
this.parameter.required(value);
}
get allowedRequiredValues() {
return this.schema.getAllowedRequiredValues();
}
get schema() {
return this.parameter.schema();
}
static getParams(target, propertyKey) {
const params = [];
const klass = ancestorsOf(target)
.reverse()
.find((target) => {
return isMethodDescriptor(target, propertyKey) && JsonEntityStore.fromMethod(target, propertyKey).children.size;
});
if (klass) {
JsonEntityStore.fromMethod(klass, propertyKey).children.forEach((param, index) => {
params[+index] = param;
});
return params;
}
return [];
}
static get(target, propertyKey, index) {
return JsonEntityStore.from(prototypeOf(target), propertyKey, index);
}
/**
* Check precondition between value, required and allowedRequiredValues to know if the entity is required.
* @param value
* @returns {boolean}
*/
isRequired(value) {
return this.required && [undefined, null, ""].includes(value) && !this.allowedRequiredValues.includes(value);
}
build() {
if (!this._type) {
const type = Metadata.getParamTypes(prototypeOf(this.target), this.propertyKey)[this.index];
this.buildType(type);
}
this._type = this._type || Object;
this.parent.children.set(this.index, this);
this.parent.operation.addParameter(this.index, this.parameter);
if (this.collectionType) {
this.parameter.schema().type(this.collectionType);
}
if (isClass(this._type)) {
this.parameter.schema().itemSchema(this._type);
}
else {
this.parameter.itemSchema().type(this._type);
}
}
};
JsonParameterStore = __decorate([
JsonEntityComponent(DecoratorTypes.PARAM),
__metadata("design:paramtypes", [Object])
], JsonParameterStore);
export { JsonParameterStore };
export const ParamMetadata = JsonParameterStore;