1- import { ForbiddenException } from '@nestjs/common' ;
2- import { Controller } from '@nestjs/common/interfaces' ;
1+ import { ForbiddenException , ParamData } from '@nestjs/common' ;
2+ import { CUSTOM_ROUTE_AGRS_METADATA } from '@nestjs/common/constants' ;
3+ import { Controller , Transform } from '@nestjs/common/interfaces' ;
4+ import { isFunction , isUndefined } from '@nestjs/common/utils/shared.utils' ;
35import 'reflect-metadata' ;
46import { FORBIDDEN_MESSAGE } from '../guards/constants' ;
57import { GuardsConsumer } from '../guards/guards-consumer' ;
@@ -8,29 +10,81 @@ import { Module } from '../injector/module';
810import { ModulesContainer } from '../injector/modules-container' ;
911import { InterceptorsConsumer } from '../interceptors/interceptors-consumer' ;
1012import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator' ;
13+ import { PipesConsumer } from '../pipes/pipes-consumer' ;
14+ import { PipesContextCreator } from '../pipes/pipes-context-creator' ;
15+ import { ContextUtils , ParamProperties } from './context-utils' ;
16+
17+ export interface ParamsMetadata {
18+ [ prop : number ] : {
19+ index : number ;
20+ data ?: ParamData ;
21+ } ;
22+ }
23+
24+ export interface ParamsFactory {
25+ exchangeKeyForValue ( type : number , data : ParamData , args : any ) : any ;
26+ }
1127
1228export class ExternalContextCreator {
29+ private readonly contextUtils = new ContextUtils ( ) ;
30+
1331 constructor (
1432 private readonly guardsContextCreator : GuardsContextCreator ,
1533 private readonly guardsConsumer : GuardsConsumer ,
1634 private readonly interceptorsContextCreator : InterceptorsContextCreator ,
1735 private readonly interceptorsConsumer : InterceptorsConsumer ,
1836 private readonly modulesContainer : ModulesContainer ,
37+ private readonly pipesContextCreator : PipesContextCreator ,
38+ private readonly pipesConsumer : PipesConsumer ,
1939 ) { }
2040
21- public create (
41+ public create < T extends ParamsMetadata = ParamsMetadata > (
2242 instance : Controller ,
2343 callback : ( ...args ) => any ,
2444 methodName : string ,
45+ metadataKey ?: string ,
46+ paramsFactory ?: ParamsFactory ,
2547 ) {
2648 const module = this . findContextModuleName ( instance . constructor ) ;
49+ const pipes = this . pipesContextCreator . create ( instance , callback , module ) ;
50+ const paramtypes = this . contextUtils . reflectCallbackParamtypes (
51+ instance ,
52+ methodName ,
53+ ) ;
2754 const guards = this . guardsContextCreator . create ( instance , callback , module ) ;
2855 const interceptors = this . interceptorsContextCreator . create (
2956 instance ,
3057 callback ,
3158 module ,
3259 ) ;
60+
61+ const metadata =
62+ this . contextUtils . reflectCallbackMetadata < T > (
63+ instance ,
64+ methodName ,
65+ metadataKey || '' ,
66+ ) || { } ;
67+ const keys = Object . keys ( metadata ) ;
68+ const argsLength = this . contextUtils . getArgumentsLength ( keys , metadata ) ;
69+ const paramsMetadata = paramsFactory
70+ ? this . exchangeKeysForValues ( keys , metadata , module , paramsFactory )
71+ : null ;
72+
73+ const paramsOptions = paramsMetadata
74+ ? this . contextUtils . mergeParamsMetatypes ( paramsMetadata , paramtypes )
75+ : [ ] ;
76+ const fnApplyPipes = this . createPipesFn ( pipes , paramsOptions ) ;
77+
78+ const handler = ( initialArgs , ...args ) => async ( ) => {
79+ if ( fnApplyPipes ) {
80+ await fnApplyPipes ( initialArgs , ...args ) ;
81+ return callback . apply ( instance , initialArgs ) ;
82+ }
83+ return callback . apply ( instance , args ) ;
84+ } ;
85+
3386 return async ( ...args ) => {
87+ const initialArgs = this . contextUtils . createNullArray ( argsLength ) ;
3488 const canActivate = await this . guardsConsumer . tryActivate (
3589 guards ,
3690 args ,
@@ -40,14 +94,14 @@ export class ExternalContextCreator {
4094 if ( ! canActivate ) {
4195 throw new ForbiddenException ( FORBIDDEN_MESSAGE ) ;
4296 }
43- const handler = ( ) => callback . apply ( instance , args ) ;
44- return await this . interceptorsConsumer . intercept (
97+ const result = await this . interceptorsConsumer . intercept (
4598 interceptors ,
4699 args ,
47100 instance ,
48101 callback ,
49- handler ,
102+ handler ( initialArgs , ... args ) ,
50103 ) ;
104+ return await this . transformToResult ( result ) ;
51105 } ;
52106 }
53107
@@ -71,4 +125,86 @@ export class ExternalContextCreator {
71125 ) ;
72126 return ! ! hasComponent ;
73127 }
128+
129+ public exchangeKeysForValues < TMetadata = any > (
130+ keys : string [ ] ,
131+ metadata : TMetadata ,
132+ moduleContext : string ,
133+ paramsFactory : ParamsFactory ,
134+ ) : ParamProperties [ ] {
135+ this . pipesContextCreator . setModuleContext ( moduleContext ) ;
136+ return keys . map ( key => {
137+ const { index, data, pipes : pipesCollection } = metadata [ key ] ;
138+ const pipes = this . pipesContextCreator . createConcreteContext (
139+ pipesCollection ,
140+ ) ;
141+ const type = this . contextUtils . mapParamType ( key ) ;
142+
143+ if ( key . includes ( CUSTOM_ROUTE_AGRS_METADATA ) ) {
144+ const { factory } = metadata [ key ] ;
145+ const customExtractValue = this . getCustomFactory ( factory , data ) ;
146+ return { index, extractValue : customExtractValue , type, data, pipes } ;
147+ }
148+ const numericType = Number ( type ) ;
149+ const extractValue = ( ...args ) =>
150+ paramsFactory . exchangeKeyForValue ( numericType , data , args ) ;
151+
152+ return { index, extractValue, type : numericType , data, pipes } ;
153+ } ) ;
154+ }
155+
156+ public getCustomFactory ( factory : ( ...args ) => void , data ) : ( ...args ) => any {
157+ return ! isUndefined ( factory ) && isFunction ( factory )
158+ ? ( ...args ) => factory ( data , args )
159+ : ( ) => null ;
160+ }
161+
162+ public createPipesFn (
163+ pipes : any [ ] ,
164+ paramsOptions : ( ParamProperties & { metatype ?: any } ) [ ] ,
165+ ) {
166+ const pipesFn = async ( args , ...gqlArgs ) => {
167+ await Promise . all (
168+ paramsOptions . map ( async param => {
169+ const {
170+ index,
171+ extractValue,
172+ type,
173+ data,
174+ metatype,
175+ pipes : paramPipes ,
176+ } = param ;
177+ const value = extractValue ( ...gqlArgs ) ;
178+
179+ args [ index ] = await this . getParamValue (
180+ value ,
181+ { metatype, type, data } ,
182+ pipes . concat ( paramPipes ) ,
183+ ) ;
184+ } ) ,
185+ ) ;
186+ } ;
187+ return paramsOptions . length ? pipesFn : null ;
188+ }
189+
190+ public async getParamValue < T > (
191+ value : T ,
192+ { metatype, type, data } ,
193+ transforms : Transform < any > [ ] ,
194+ ) : Promise < any > {
195+ return await this . pipesConsumer . apply (
196+ value ,
197+ { metatype, type, data } ,
198+ transforms ,
199+ ) ;
200+ }
201+
202+ public async transformToResult ( resultOrDeffered ) {
203+ if ( resultOrDeffered instanceof Promise ) {
204+ return await resultOrDeffered ;
205+ } else if ( resultOrDeffered && isFunction ( resultOrDeffered . subscribe ) ) {
206+ return await resultOrDeffered . toPromise ( ) ;
207+ }
208+ return resultOrDeffered ;
209+ }
74210}
0 commit comments