1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const constants_1 = require("@nestjs/common/constants");
|
4 | const exceptions_1 = require("@nestjs/common/exceptions");
|
5 | const logger_service_1 = require("@nestjs/common/services/logger.service");
|
6 | const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
|
7 | const pathToRegexp = require("path-to-regexp");
|
8 | const unknown_request_mapping_exception_1 = require("../errors/exceptions/unknown-request-mapping.exception");
|
9 | const guards_consumer_1 = require("../guards/guards-consumer");
|
10 | const guards_context_creator_1 = require("../guards/guards-context-creator");
|
11 | const context_id_factory_1 = require("../helpers/context-id-factory");
|
12 | const execution_context_host_1 = require("../helpers/execution-context-host");
|
13 | const messages_1 = require("../helpers/messages");
|
14 | const router_method_factory_1 = require("../helpers/router-method-factory");
|
15 | const constants_2 = require("../injector/constants");
|
16 | const interceptors_consumer_1 = require("../interceptors/interceptors-consumer");
|
17 | const interceptors_context_creator_1 = require("../interceptors/interceptors-context-creator");
|
18 | const pipes_consumer_1 = require("../pipes/pipes-consumer");
|
19 | const pipes_context_creator_1 = require("../pipes/pipes-context-creator");
|
20 | const request_constants_1 = require("./request/request-constants");
|
21 | const route_params_factory_1 = require("./route-params-factory");
|
22 | const router_execution_context_1 = require("./router-execution-context");
|
23 | class RouterExplorer {
|
24 | constructor(metadataScanner, container, injector, routerProxy, exceptionsFilter, config) {
|
25 | this.metadataScanner = metadataScanner;
|
26 | this.container = container;
|
27 | this.injector = injector;
|
28 | this.routerProxy = routerProxy;
|
29 | this.exceptionsFilter = exceptionsFilter;
|
30 | this.routerMethodFactory = new router_method_factory_1.RouterMethodFactory();
|
31 | this.logger = new logger_service_1.Logger(RouterExplorer.name, true);
|
32 | this.exceptionFiltersCache = new WeakMap();
|
33 | this.executionContextCreator = new router_execution_context_1.RouterExecutionContext(new route_params_factory_1.RouteParamsFactory(), new pipes_context_creator_1.PipesContextCreator(container, config), new pipes_consumer_1.PipesConsumer(), new guards_context_creator_1.GuardsContextCreator(container, config), new guards_consumer_1.GuardsConsumer(), new interceptors_context_creator_1.InterceptorsContextCreator(container, config), new interceptors_consumer_1.InterceptorsConsumer(), container.getHttpAdapterRef());
|
34 | }
|
35 | explore(instanceWrapper, module, applicationRef, basePath, host) {
|
36 | const { instance } = instanceWrapper;
|
37 | const routerPaths = this.scanForPaths(instance);
|
38 | this.applyPathsToRouterProxy(applicationRef, routerPaths, instanceWrapper, module, basePath, host);
|
39 | }
|
40 | extractRouterPath(metatype, prefix) {
|
41 | let path = Reflect.getMetadata(constants_1.PATH_METADATA, metatype);
|
42 | if (prefix)
|
43 | path = prefix + this.validateRoutePath(path);
|
44 | return this.validateRoutePath(path);
|
45 | }
|
46 | validateRoutePath(path) {
|
47 | if (shared_utils_1.isUndefined(path)) {
|
48 | throw new unknown_request_mapping_exception_1.UnknownRequestMappingException();
|
49 | }
|
50 | return shared_utils_1.validatePath(path);
|
51 | }
|
52 | scanForPaths(instance, prototype) {
|
53 | const instancePrototype = shared_utils_1.isUndefined(prototype)
|
54 | ? Object.getPrototypeOf(instance)
|
55 | : prototype;
|
56 | return this.metadataScanner.scanFromPrototype(instance, instancePrototype, method => this.exploreMethodMetadata(instance, instancePrototype, method));
|
57 | }
|
58 | exploreMethodMetadata(instance, instancePrototype, methodName) {
|
59 | const targetCallback = instancePrototype[methodName];
|
60 | const routePath = Reflect.getMetadata(constants_1.PATH_METADATA, targetCallback);
|
61 | if (shared_utils_1.isUndefined(routePath)) {
|
62 | return null;
|
63 | }
|
64 | const requestMethod = Reflect.getMetadata(constants_1.METHOD_METADATA, targetCallback);
|
65 | const path = shared_utils_1.isString(routePath)
|
66 | ? [this.validateRoutePath(routePath)]
|
67 | : routePath.map(p => this.validateRoutePath(p));
|
68 | return {
|
69 | path,
|
70 | requestMethod,
|
71 | targetCallback,
|
72 | methodName,
|
73 | };
|
74 | }
|
75 | applyPathsToRouterProxy(router, routePaths, instanceWrapper, module, basePath, host) {
|
76 | (routePaths || []).forEach(pathProperties => {
|
77 | const { path, requestMethod } = pathProperties;
|
78 | this.applyCallbackToRouter(router, pathProperties, instanceWrapper, module, basePath, host);
|
79 | path.forEach(p => this.logger.log(messages_1.ROUTE_MAPPED_MESSAGE(p, requestMethod)));
|
80 | });
|
81 | }
|
82 | applyCallbackToRouter(router, pathProperties, instanceWrapper, moduleKey, basePath, host) {
|
83 | const { path: paths, requestMethod, targetCallback, methodName, } = pathProperties;
|
84 | const { instance } = instanceWrapper;
|
85 | const routerMethod = this.routerMethodFactory
|
86 | .get(router, requestMethod)
|
87 | .bind(router);
|
88 | const stripSlash = (str) => str[str.length - 1] === '/' ? str.slice(0, str.length - 1) : str;
|
89 | const isRequestScoped = !instanceWrapper.isDependencyTreeStatic();
|
90 | const proxy = isRequestScoped
|
91 | ? this.createRequestScopedHandler(instanceWrapper, requestMethod, this.container.getModuleByKey(moduleKey), moduleKey, methodName)
|
92 | : this.createCallbackProxy(instance, targetCallback, methodName, moduleKey, requestMethod);
|
93 | const hostHandler = this.applyHostFilter(host, proxy);
|
94 | paths.forEach(path => {
|
95 | const fullPath = stripSlash(basePath) + path;
|
96 | routerMethod(stripSlash(fullPath) || '/', hostHandler);
|
97 | });
|
98 | }
|
99 | applyHostFilter(host, handler) {
|
100 | if (!host) {
|
101 | return handler;
|
102 | }
|
103 | const httpAdapterRef = this.container.getHttpAdapterRef();
|
104 | const keys = [];
|
105 | const re = pathToRegexp(host, keys);
|
106 | return (req, res, next) => {
|
107 | req.hosts = {};
|
108 | const hostname = httpAdapterRef.getRequestHostname(req) || '';
|
109 | const match = hostname.match(re);
|
110 | if (match) {
|
111 | keys.forEach((key, i) => (req.hosts[key.name] = match[i + 1]));
|
112 | return handler(req, res, next);
|
113 | }
|
114 | if (!next) {
|
115 | throw new exceptions_1.InternalServerErrorException(`HTTP adapter does not support filtering on host: "${host}"`);
|
116 | }
|
117 | return next();
|
118 | };
|
119 | }
|
120 | createCallbackProxy(instance, callback, methodName, module, requestMethod, contextId = constants_2.STATIC_CONTEXT, inquirerId) {
|
121 | const executionContext = this.executionContextCreator.create(instance, callback, methodName, module, requestMethod, contextId, inquirerId);
|
122 | const exceptionFilter = this.exceptionsFilter.create(instance, callback, module, contextId, inquirerId);
|
123 | return this.routerProxy.createProxy(executionContext, exceptionFilter);
|
124 | }
|
125 | createRequestScopedHandler(instanceWrapper, requestMethod, module, moduleKey, methodName) {
|
126 | const { instance } = instanceWrapper;
|
127 | const collection = module.controllers;
|
128 | return async (req, res, next) => {
|
129 | try {
|
130 | const contextId = this.getContextId(req);
|
131 | const contextInstance = await this.injector.loadPerContext(instance, module, collection, contextId);
|
132 | await this.createCallbackProxy(contextInstance, contextInstance[methodName], methodName, moduleKey, requestMethod, contextId, instanceWrapper.id)(req, res, next);
|
133 | }
|
134 | catch (err) {
|
135 | let exceptionFilter = this.exceptionFiltersCache.get(instance[methodName]);
|
136 | if (!exceptionFilter) {
|
137 | exceptionFilter = this.exceptionsFilter.create(instance, instance[methodName], moduleKey);
|
138 | this.exceptionFiltersCache.set(instance[methodName], exceptionFilter);
|
139 | }
|
140 | const host = new execution_context_host_1.ExecutionContextHost([req, res, next]);
|
141 | exceptionFilter.next(err, host);
|
142 | }
|
143 | };
|
144 | }
|
145 | getContextId(request) {
|
146 | const contextId = context_id_factory_1.ContextIdFactory.getByRequest(request);
|
147 | if (!request[request_constants_1.REQUEST_CONTEXT_ID]) {
|
148 | Object.defineProperty(request, request_constants_1.REQUEST_CONTEXT_ID, {
|
149 | value: contextId,
|
150 | enumerable: false,
|
151 | writable: false,
|
152 | configurable: false,
|
153 | });
|
154 | this.container.registerRequestProvider(request, contextId);
|
155 | }
|
156 | return contextId;
|
157 | }
|
158 | }
|
159 | exports.RouterExplorer = RouterExplorer;
|