import type { Type } from '@nestjs/common' ; import { applyDecorators , UseInterceptors } from '@nestjs/common' ; import { PARAMTYPES_METADATA , ROUTE_ARGS_METADATA , } from '@nestjs/common/constants' ; import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum' ; import { FileInterceptor } from '@nestjs/platform-express' ; import { ApiBody , ApiConsumes , ApiExtraModels , getSchemaPath , } from '@nestjs/swagger' ; import type { ReferenceObject , SchemaObject , } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface' ; import { reverseObjectKeys } from '@nestjs/swagger/dist/utils/reverse-object-keys.util' ; import _ from 'lodash' ; import type { IApiFile } from '../interfaces' ; function explore ( instance , propertyKey ) { const types = Reflect . getMetadata ( PARAMTYPES_METADATA , instance , propertyKey ) ; const routeArgsMetadata = Reflect . getMetadata ( ROUTE_ARGS_METADATA , instance . constructor , propertyKey ) || { } ; const parametersWithType = _ . mapValues ( reverseObjectKeys ( routeArgsMetadata ) , ( param ) => ( { type : types [ param . index ] , name : param . data , required : true , } ) ) ; for ( const [ key , value ] of Object . entries ( parametersWithType ) ) { const keyPair = key . split ( ':' ) ; if ( Number ( keyPair [ 0 ] ) === RouteParamtypes . BODY ) { return value . type ; } } } function RegisterModels ( ) { return ( target , propertyKey , descriptor ) => { const body = explore ( target , propertyKey ) ; return body && ApiExtraModels ( body ) ( target , propertyKey , descriptor ) ; } ; } function ApiFileDecorator ( files = [ ] , options = { } ) { return ( target , propertyKey , descriptor ) => { const { isRequired = false } = options ; const fileSchema = { type : 'string' , format : 'binary' , } ; const properties = { } ; for ( const file of files ) { if ( file ?. isArray ) { properties [ file . name ] = { type : 'array' , items : fileSchema , } ; } else { properties [ file . name ] = fileSchema ; } } let schema = { properties , type : 'object' , } ; const body = explore ( target , propertyKey ) ; if ( body ) { schema = { allOf : [ { $ref : getSchemaPath ( body ) , } , { properties , type : 'object' } , ] , } ; } return ApiBody ( { schema , required : isRequired , } ) ( target , propertyKey , descriptor ) ; } ; } export function ApiFile ( files , options = { } ) { const filesArray = _ . castArray ( files ) ; const apiFileInterceptors = filesArray . map ( ( file ) => UseInterceptors ( FileInterceptor ( file . name ) ) ) ; return applyDecorators ( RegisterModels ( ) , ApiConsumes ( 'multipart/form-data' ) , ApiFileDecorator ( filesArray , options ) , ... apiFileInterceptors ) ; }