import "reflect-metadata" ; import { EntityManager , getMetadataArgsStorage as getTypeormMetadataArgsStorage , QueryRunner } from "typeorm" ; import { Action } from "./interface/Action" ; import { SchemaBuilder } from "./SchemaBuilder" ; import { ActionMetadata } from "./metadata/ActionMetadata" ; import { Container } from "typedi" ; /**
 * Executes action in multiple steps.
 * While we could do it in a single step using async/await syntax we decided to
 * make core complex code in favour of less ticks to improve performance.
 */ export class ActionExecutor { constructor ( protected builder ) { } /**
     * Executes controller action.
     */ executeControllerAction ( action ) { return this . step1 ( action ) ; } /**
     * Executes resolver action.
     */ executeResolver ( action ) { return this . step3 ( action ) ; } /**
     * First step - check if action must run in a transaction.
     */ protected step1 ( action ) { if ( this . builder . connection ) { if ( ( action . metadata as ActionMetadata ) . transaction && this . builder . connection . options . type !== "mongodb" ) { const containerEntityManager = action . context . container . get ( EntityManager ) ; let queryRunner ; if ( containerEntityManager && containerEntityManager . queryRunner && containerEntityManager . queryRunner . isReleased === false ) { queryRunner = containerEntityManager . queryRunner ; } else { queryRunner = this . builder . connection . createQueryRunner ( ) ; } if ( queryRunner . isTransactionActive === false ) { return queryRunner . startTransaction ( ) . then ( ( ) => { return this . step2 ( action , queryRunner . manager ) ; } ) ; } } else { return this . step2 ( action , this . builder . connection . manager ) ; } } return this . step2 ( action ) ; } /**
     * Second step - setup container.
     */ protected step2 ( action , entityManager ? ) { action . context . container . set ( Container , action . context . container ) ; if ( entityManager ) { action . context . container . set ( EntityManager , entityManager ) ; getTypeormMetadataArgsStorage ( ) . entityRepositories . forEach ( repository => { action . context . container . set ( repository . target , entityManager . getCustomRepository ( repository . target ) ) ; } ) ; } if ( this . builder . options . setupContainer ) { const setupContainerResult = this . builder . options . setupContainer ( action . container , action ) ; if ( setupContainerResult instanceof Promise ) return setupContainerResult . then ( ( ) => this . step3 ( action ) ) ; } return this . step3 ( action ) ; } /**
     * Third step - check authorization.
     */ protected step3 ( action ) { let promiseResults = [ ] ; action . metadata . authorizes . forEach ( authorize => { let authorizationCheckResult ; if ( authorize instanceof Array ) { authorizationCheckResult = this . builder . options . authorizationChecker ( authorize , action ) ; } else { authorizationCheckResult = action . container . get ( authorize ) . check ( action ) ; } if ( authorizationCheckResult instanceof Promise ) promiseResults . push ( authorizationCheckResult ) ; } ) ; if ( promiseResults . length > 0 ) return Promise . all ( promiseResults ) . then ( result => this . step4 ( action ) ) ; return this . step4 ( action ) ; } /**
     * Forth step - validate action args.
     */ protected step4 ( action ) { let promiseResults = [ ] ; action . metadata . validators . forEach ( validator => { const validationResult = action . container . get ( validator ) . validate ( action . args ) ; if ( validationResult instanceof Promise ) promiseResults . push ( validationResult ) ; } ) ; if ( promiseResults . length > 0 ) return Promise . all ( promiseResults ) . then ( result => this . step5 ( action ) ) ; return this . step5 ( action ) ; } /**
     * Fifth step - execute controller / resolver method.
     */ protected step5 ( action ) { if ( ( action . metadata as ActionMetadata ) . type === "query" || ( action . metadata as ActionMetadata ) . type === "mutation" ) { return action . container . get < any > ( action . metadata . target ) [ action . metadata . methodName ] ( action . args , action . context , action . info ) ; } else { return action . container . get < any > ( action . metadata . target ) [ action . metadata . methodName ] ( action . obj , action . args , action . context , action . info ) ; } } }