@tsed/schema
Version:
JsonSchema module for Ts.ED Framework
181 lines (180 loc) • 4.48 kB
JavaScript
import { withErrorMsg } from "../../utils/withErrorMsg.js";
import { JsonEntityFn } from "../common/jsonEntityFn.js";
/**
* Sets the minimum number of properties required on an object.
*
* The `@MinProperties()` decorator validates that an object has at least the specified
* number of properties. This is useful for ensuring objects aren't empty, enforcing
* minimum data requirements, or validating that dynamic objects have sufficient content.
*
* ### On Property
*
* ```typescript
* class DataModel {
* @MinProperties(1)
* attributes: Record<string, any>;
* // Object must have at least 1 property
* }
* ```
*
* Generated schema:
* ```json
* {
* "type": "object",
* "properties": {
* "attributes": {
* "type": "object",
* "minProperties": 1
* }
* }
* }
* ```
*
* ### On Class
*
* ```typescript
* @MinProperties(2)
* class RequiredDataModel {
* // Class instances must have at least 2 properties
* }
* ```
*
* Generated schema:
* ```json
* {
* "type": "object",
* "minProperties": 2
* }
* ```
*
* ### On Parameter
*
* ```typescript
* @Controller("/api")
* class ApiController {
* @Post("/filters")
* search(
* @BodyParams() @MinProperties(1) filters: any
* ) {
* // Filters object must have at least 1 property
* }
* }
* ```
*
* ### With MaxProperties - Range Validation
*
* ```typescript
* class MetadataModel {
* @MinProperties(2)
* @MaxProperties(10)
* metadata: Record<string, string>;
* // Must have between 2 and 10 properties
* }
* ```
*
* ### Non-Empty Objects
*
* ```typescript
* class SearchCriteria {
* @MinProperties(1)
* criteria: Record<string, any>;
* // Prevent empty search criteria objects
* }
* ```
*
* ### Dynamic Configuration
*
* ```typescript
* class PluginConfig {
* @MinProperties(1)
* @AdditionalProperties({ type: "string" })
* options: Record<string, string>;
* // At least 1 configuration option required
* }
* ```
*
* ### Validation Examples
*
* ```typescript
* @MinProperties(2)
* data: object;
*
* // Valid:
* { a: 1, b: 2 } // 2 properties (≥ 2)
* { a: 1, b: 2, c: 3 } // 3 properties (≥ 2)
*
* // Invalid:
* {} // 0 properties (< 2)
* { a: 1 } // 1 property (< 2)
* ```
*
* ### Use Cases
*
* - **Required Data**: Ensure objects have minimum content
* - **Non-Empty Objects**: Prevent empty dynamic objects
* - **Business Rules**: Enforce minimum configuration requirements
* - **API Validation**: Require sufficient filter/search criteria
* - **Data Quality**: Ensure objects contain meaningful data
*
* ### Filter Validation
*
* ```typescript
* class QueryModel {
* @MinProperties(1)
* filters: Record<string, string>;
* // Must specify at least one filter
* }
* ```
*
* ### Combining with AdditionalProperties
*
* ```typescript
* class FlexibleModel {
* @Property()
* id: string;
*
* @MinProperties(3)
* @AdditionalProperties({ type: "string" })
* // 1 defined property + at least 2 additional properties = 3 minimum
* }
* ```
*
* ### Important Notes
*
* - The value must be a **non-negative integer** (0 or greater)
* - Throws an error if a negative value is provided
* - Omitting `@MinProperties()` is equivalent to `@MinProperties(0)` (no minimum)
* - Counts all properties, including defined and additional ones
* - Objects with exactly minProperties count are valid (inclusive)
* - Can be combined with custom error messages using ajv-errors
*
* ### Default Behavior
*
* Without `@MinProperties()`, empty objects are valid:
* ```typescript
* data: Record<string, any>;
* // Valid: {}, { a: 1 }, { a: 1, b: 2 }, etc.
* ```
*
* With `@MinProperties(1)`, empty objects are rejected:
* ```typescript
* @MinProperties(1)
* data: Record<string, any>;
* // Invalid: {}
* // Valid: { a: 1 }, { a: 1, b: 2 }, etc.
* ```
*
* @param minProperties - Minimum number of properties required (must be non-negative)
*
* @decorator
* @validation
* @public
*/
export const MinProperties = withErrorMsg("minProperties", (minProperties) => {
if (minProperties < 0) {
throw new Error("The value of minProperties MUST be a non-negative integer.");
}
return JsonEntityFn((store) => {
store.isCollection ? store.schema.minProperties(minProperties) : store.itemSchema.minProperties(minProperties);
});
});