import * as cv from 'class-validator' ; import { ConstraintMetadata } from 'class-validator/types/metadata/ConstraintMetadata' ; import { ValidationMetadata } from 'class-validator/types/metadata/ValidationMetadata' ; import _groupBy from 'lodash.groupby' ; import _merge from 'lodash.merge' ; import { SchemaObject } from 'openapi3-ts' ; import { getMetadataSchema } from './decorators' ; import { defaultConverters } from './defaultConverters' ; import { defaultOptions , IOptions } from './options' ; export { JSONSchema } from './decorators' ; /**
 * Convert class-validator metadata into JSON Schema definitions.
 */ export function validationMetadatasToSchemas ( userOptions ? ) { const options = { ... defaultOptions , ... userOptions , } ; const metadatas = getMetadatasFromStorage ( options . classValidatorMetadataStorage ) ; return validationMetadataArrayToSchemas ( metadatas , userOptions ) ; } /**
 * Convert an array of class-validator metadata into JSON Schema definitions.
 */ export function validationMetadataArrayToSchemas ( metadatas , userOptions ? ) { const options = { ... defaultOptions , ... userOptions , } ; const schemas = { } ; Object . entries ( _groupBy ( metadatas , ( { target } ) => target [ options . schemaNameField as keyof typeof target ] ?? ( target as Function ) . name ) ) . forEach ( ( [ key , ownMetas ] ) => { const target = ownMetas [ 0 ] . target as Function ; const metas = ownMetas . concat ( getInheritedMetadatas ( target , metadatas ) ) . filter ( ( propMeta ) => ! isExcluded ( propMeta , options ) ) ; const properties = { } ; Object . entries ( _groupBy ( metas , 'propertyName' ) ) . forEach ( ( [ propName , propMetas ] ) => { const schema = applyConverters ( propMetas , options ) ; properties [ propName ] = applyDecorators ( schema , target , options , propName ) ; } ) ; const definitionSchema = { properties , type : 'object' , } ; const required = getRequiredPropNames ( target , metas , options ) ; if ( required . length > 0 ) { definitionSchema . required = required ; } schemas [ key ] = applyDecorators ( definitionSchema , target , options , target . name ) ; } ) ; return schemas ; } /**
 * Generate JSON Schema definitions from the target object constructor.
 */ export function targetConstructorToSchema ( targetConstructor , userOptions ? ) { const options = { ... defaultOptions , ... userOptions , } ; const storage = options . classValidatorMetadataStorage ; let metadatas = storage . getTargetValidationMetadatas ( targetConstructor , '' , true , false ) ; metadatas = populateMetadatasWithConstraints ( storage , metadatas ) ; const schemas = validationMetadataArrayToSchemas ( metadatas , userOptions ) ; return Object . values ( schemas ) . length ? Object . values ( schemas ) [ 0 ] : { } ; } /**
 * Return `storage.validationMetadatas` populated with `constraintMetadatas`.
 */ function getMetadatasFromStorage ( storage ) { const metadatas = ( storage as any ) . validationMetadatas ; return populateMetadatasWithConstraints ( storage , metadatas ) ; } function populateMetadatasWithConstraints ( storage , metadatas ) { const constraints = ( storage as any ) . constraintMetadatas ; return metadatas . map ( ( meta ) => { if ( meta . constraintCls ) { const constraint = constraints . find ( ( c ) => c . target === meta . constraintCls ) ; if ( constraint ) { return { ... meta , type : constraint . name } ; } } return meta ; } ) ; } param target param metadatas function getInheritedMetadatas ( target , metadatas ) { return metadatas . filter ( ( d ) => d . target instanceof Function && target . prototype instanceof d . target && ! metadatas . find ( ( m ) => m . propertyName === d . propertyName && m . target === target && m . type === d . type ) ) ; } /**
 * Convert a property's class-validator metadata into a JSON Schema property.
 */ function applyConverters ( propertyMetadatas , options ) { const converters = { ... defaultConverters , ... options . additionalConverters } ; const convert = ( meta ) => { const typeMeta = options . classTransformerMetadataStorage ?. findTypeMetadata ( meta . target as Function , meta . propertyName ) ; const isMap = typeMeta && typeMeta . reflectedType && new typeMeta . reflectedType ( ) instanceof Map ; const converter = converters [ meta . type ] || converters [ cv . ValidationTypes . CUSTOM_VALIDATION ] ; const items = typeof converter === 'function' ? converter ( meta , options ) : converter ; if ( meta . each && isMap ) { return { additionalProperties : { ... items , } , type : 'object' , } ; } return meta . each ? { items , type : 'array' } : items ; } ; return _merge ( { } , ... propertyMetadatas . map ( convert ) ) ; } /** Check whether property is excluded with class-transformer `@Exclude` decorator. */ function isExcluded ( propertyMetadata , options ) { return ! ! options . classTransformerMetadataStorage ?. findExcludeMetadata ( propertyMetadata . target as Function , propertyMetadata . propertyName ) ; } /**
 * Given a JSON Schema object, supplement it with additional schema properties
 * defined by target object's @JSONSchema decorator.
 */ function applyDecorators ( schema , target , options , propertyName ) { const additionalSchema = getMetadataSchema ( target . prototype , propertyName ) ; return typeof additionalSchema === 'function' ? additionalSchema ( schema , options ) : _merge ( { } , schema , additionalSchema ) ; } param target param metadatas param options function getRequiredPropNames ( target , metadatas , options ) { function isDefined ( metas ) { return ( metas && metas . some ( ( { type } ) => type === cv . ValidationTypes . IS_DEFINED ) ) ; } function isOptional ( metas ) { return ( metas && metas . some ( ( { type } ) => [ cv . ValidationTypes . CONDITIONAL_VALIDATION , cv . IS_EMPTY ] . includes ( type ) ) ) ; } return Object . entries ( _groupBy ( metadatas , ( m ) => m . propertyName ) ) . filter ( ( [ _ , metas ] ) => { const own = metas . filter ( ( m ) => m . target === target ) ; const inherited = metas . filter ( ( m ) => m . target !== target ) ; return options . skipMissingProperties ? isDefined ( own ) || ( ! isOptional ( own ) && isDefined ( inherited ) ) : ! ( isOptional ( own ) || isOptional ( inherited ) ) ; } ) . map ( ( [ name ] ) => name ) ; }