UNPKG

@elsikora/nestjs-crud-automator

Version:

A library for automating the creation of CRUD operations in NestJS.

189 lines (185 loc) 12.4 kB
'use strict'; var contextStorage_class = require('../function/context-storage.class.js'); var registry_class = require('./registry.class.js'); var controller_constant = require('../../../constant/decorator/api/controller.constant.js'); var service_constant = require('../../../constant/decorator/api/service.constant.js'); require('../../../enum/decorator/api/action.enum.js'); require('../../../enum/decorator/api/authentication-type.enum.js'); require('../../../enum/decorator/api/controller/get-list/query/filter/missing-behavior.enum.js'); require('../../../enum/decorator/api/controller/get-list/query/unlisted-fields.enum.js'); require('../../../enum/decorator/api/controller/relation-reference-shape.enum.js'); require('../../../enum/decorator/api/controller/request/target.enum.js'); require('../../../enum/decorator/api/controller/request/transformer-type.enum.js'); require('../../../enum/decorator/api/controller/response-target.enum.js'); require('../../../enum/decorator/api/dto-type.enum.js'); require('../../../enum/decorator/api/function/context-storage-kind.enum.js'); var subscriberTransactionExpectation_enum = require('../../../enum/decorator/api/function/subscriber-transaction-expectation.enum.js'); require('../../../enum/decorator/api/function/transaction/event-status.enum.js'); require('../../../enum/decorator/api/function/transaction/failure-stage.enum.js'); require('../../../enum/decorator/api/function/transaction/mode.enum.js'); require('../../../enum/decorator/api/function/transaction/outcome.enum.js'); require('../../../enum/decorator/api/function/transaction/owner-kind.enum.js'); require('../../../enum/decorator/api/function/transaction/trace-type.enum.js'); var type_enum = require('../../../enum/decorator/api/function/type.enum.js'); var onType_enum = require('../../../enum/decorator/api/on-type.enum.js'); require('../../../enum/decorator/api/property/data-type.enum.js'); require('../../../enum/decorator/api/property/date/identifier.enum.js'); require('../../../enum/decorator/api/property/date/type.enum.js'); require('../../../enum/decorator/api/property/desribe-type.enum.js'); require('../../../enum/decorator/api/property/number-type.enum.js'); require('../../../enum/decorator/api/property/string-type.enum.js'); require('../../../enum/decorator/api/route/subscriber-authorization-expectation.enum.js'); require('../../../enum/decorator/api/route/type.enum.js'); var camelCaseString_utility = require('../../../utility/camel-case-string.utility.js'); var exception_utility = require('../../../utility/error/exception.utility.js'); var logger_utility = require('../../../utility/logger.utility.js'); const subscriberLogger = logger_utility.LoggerUtility.getLogger("ApiSubscriberExecutor"); class ApiSubscriberExecutor { static async executeFunctionBeforeSubscribers(constructor, entity, functionType, context, action) { try { const result = await ApiSubscriberExecutor.executeFunctionSubscribers(constructor, entity, functionType, onType_enum.EApiSubscriberOnType.BEFORE, context, action); ApiSubscriberExecutor.assertFunctionSubscribersTransactionExpectation(constructor, entity, functionType, onType_enum.EApiSubscriberOnType.AFTER, context, action); return result; } catch (error) { const errorExecutionContext = { action: context.action, DATA: context.DATA, ENTITY: context.ENTITY, FUNCTION_TYPE: context.FUNCTION_TYPE, }; await ApiSubscriberExecutor.executeFunctionErrorSubscribers(constructor, entity, functionType, onType_enum.EApiSubscriberOnType.BEFORE_ERROR, errorExecutionContext, error, action ?? context.action); throw error; } } static async executeFunctionErrorSubscribers(constructor, entity, functionType, onType, context, error, action) { if (!Reflect.hasMetadata(service_constant.SERVICE_API_DECORATOR_CONSTANT.OBSERVABLE_METADATA_KEY, constructor)) { return; } const entityName = ApiSubscriberExecutor.resolveEntityName(entity, context); const subscribers = registry_class.apiSubscriberRegistry.getFunctionSubscribers(entityName, functionType, action ?? context.action); await contextStorage_class.ApiFunctionContextStorage.runWithoutStepContext(async () => { for (const subscriber of subscribers) { const hookName = ApiSubscriberExecutor.resolveHookName(onType, functionType); const hook = Reflect.get(subscriber, hookName); if (typeof hook === "function") { const properties = registry_class.apiSubscriberRegistry.getFunctionSubscriberProperties(subscriber); ApiSubscriberExecutor.assertFunctionSubscriberTransactionExpectation(subscriber.constructor.name, properties, context); subscriberLogger.verbose(`Executing function error hook ${hookName} from ${subscriber.constructor.name} for entity ${entityName}`); await hook.call(subscriber, context, error); } } }); } static async executeFunctionSubscribers(constructor, entity, functionType, onType, context, action) { if (!Reflect.hasMetadata(service_constant.SERVICE_API_DECORATOR_CONSTANT.OBSERVABLE_METADATA_KEY, constructor)) { return context.result; } const entityName = ApiSubscriberExecutor.resolveEntityName(entity, context); const subscribers = registry_class.apiSubscriberRegistry.getFunctionSubscribers(entityName, functionType, action ?? context.action); let result = context.result; await contextStorage_class.ApiFunctionContextStorage.runWithoutStepContext(async () => { for (const subscriber of subscribers) { const hookName = ApiSubscriberExecutor.resolveHookName(onType, functionType); const hook = Reflect.get(subscriber, hookName); if (typeof hook === "function") { const properties = registry_class.apiSubscriberRegistry.getFunctionSubscriberProperties(subscriber); ApiSubscriberExecutor.assertFunctionSubscriberTransactionExpectation(subscriber.constructor.name, properties, context); subscriberLogger.verbose(`Executing function hook ${hookName} from ${subscriber.constructor.name} for entity ${entityName}`); const hookResult = (await hook.call(subscriber, { ...context, result })); if (hookResult !== undefined) { result = hookResult; } } } }); return result; } static async executeRouteErrorSubscribers(constructor, entity, routeType, onType, context, error, action) { if (!Reflect.hasMetadata(controller_constant.CONTROLLER_API_DECORATOR_CONSTANT.OBSERVABLE_METADATA_KEY, constructor)) { return; } const entityName = ApiSubscriberExecutor.resolveEntityName(entity, context); const subscribers = registry_class.apiSubscriberRegistry.getRouteSubscribers(entityName, constructor, routeType, action ?? context.action); for (const subscriber of subscribers) { const hookName = ApiSubscriberExecutor.resolveHookName(onType, routeType); const hook = Reflect.get(subscriber, hookName); if (typeof hook === "function") { subscriberLogger.verbose(`Executing route error hook ${hookName} from ${subscriber.constructor.name} for entity ${entityName}`); await hook.call(subscriber, context, error); } } } static async executeRouteSubscribers(constructor, entity, routeType, onType, context, action) { if (!Reflect.hasMetadata(controller_constant.CONTROLLER_API_DECORATOR_CONSTANT.OBSERVABLE_METADATA_KEY, constructor)) { return context.result; } const entityName = ApiSubscriberExecutor.resolveEntityName(entity, context); const subscribers = registry_class.apiSubscriberRegistry.getRouteSubscribers(entityName, constructor, routeType, action ?? context.action); let result = context.result; for (const subscriber of subscribers) { const hookName = ApiSubscriberExecutor.resolveHookName(onType, routeType); const hook = Reflect.get(subscriber, hookName); if (typeof hook === "function") { subscriberLogger.verbose(`Executing route hook ${hookName} from ${subscriber.constructor.name} for entity ${entityName}`); const hookResult = (await hook.call(subscriber, { ...context, result })); if (hookResult !== undefined) { result = hookResult; } } } return result; } static assertFunctionSubscribersTransactionExpectation(constructor, entity, functionType, onType, context, action) { if (!Reflect.hasMetadata(service_constant.SERVICE_API_DECORATOR_CONSTANT.OBSERVABLE_METADATA_KEY, constructor)) { return; } const entityName = ApiSubscriberExecutor.resolveEntityName(entity, context); const subscribers = registry_class.apiSubscriberRegistry.getFunctionSubscribers(entityName, functionType, action ?? context.action); for (const subscriber of subscribers) { if (!ApiSubscriberExecutor.hasFunctionSubscriberHook(subscriber, onType, functionType)) { continue; } const properties = registry_class.apiSubscriberRegistry.getFunctionSubscriberProperties(subscriber); ApiSubscriberExecutor.assertFunctionSubscriberTransactionExpectation(subscriber.constructor.name, properties, context); } } static assertFunctionSubscriberTransactionExpectation(subscriberName, properties, context) { const transactionExpectation = properties?.transaction?.expectation; if (transactionExpectation !== subscriberTransactionExpectation_enum.EApiFunctionSubscriberTransactionExpectation.MANDATORY && transactionExpectation !== subscriberTransactionExpectation_enum.EApiFunctionSubscriberTransactionExpectation.REQUIRED) { return; } const eventManager = ApiSubscriberExecutor.resolveFunctionSubscriberEventManager(context); if (eventManager === null || eventManager === undefined) { throw exception_utility.ErrorException(`Function subscriber "${subscriberName}" declares transaction expectation "${transactionExpectation}" but no event manager was provided`); } } static hasFunctionSubscriberHook(subscriber, onType, functionType) { return typeof subscriber[ApiSubscriberExecutor.resolveHookName(onType, functionType)] === "function"; } static resolveEntityName(entity, context) { const data = context?.DATA; const entityMetadataName = data && typeof data === "object" && "entityMetadata" in data ? data.entityMetadata?.name : undefined; if (typeof entityMetadataName === "string" && entityMetadataName.length > 0) { return entityMetadataName; } const propertyEntityName = data && typeof data === "object" && "properties" in data ? data.properties?.entity?.name : undefined; if (typeof propertyEntityName === "string" && propertyEntityName.length > 0) { return propertyEntityName; } const repositoryName = data && typeof data === "object" && "repository" in data ? (data.repository?.metadata?.name ?? data.repository?.target?.name) : undefined; if (typeof repositoryName === "string" && repositoryName.length > 0) { return repositoryName; } return entity.constructor.name; } static resolveFunctionSubscriberEventManager(context) { const data = context.DATA; return data && typeof data === "object" && "eventManager" in data ? data.eventManager : undefined; } static resolveHookName(onType, lifecycleType) { return lifecycleType === undefined || lifecycleType === type_enum.EApiFunctionType.CUSTOM ? `on${onType}Custom` : `on${onType}${camelCaseString_utility.CamelCaseString(lifecycleType)}`; } } exports.ApiSubscriberExecutor = ApiSubscriberExecutor; //# sourceMappingURL=executor.class.js.map