UNPKG

16.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.RouterExplorer = void 0;
4const constants_1 = require("@nestjs/common/constants");
5const version_type_enum_1 = require("@nestjs/common/enums/version-type.enum");
6const exceptions_1 = require("@nestjs/common/exceptions");
7const version_options_interface_1 = require("@nestjs/common/interfaces/version-options.interface");
8const logger_service_1 = require("@nestjs/common/services/logger.service");
9const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
10const pathToRegexp = require("path-to-regexp");
11const unknown_request_mapping_exception_1 = require("../errors/exceptions/unknown-request-mapping.exception");
12const guards_consumer_1 = require("../guards/guards-consumer");
13const guards_context_creator_1 = require("../guards/guards-context-creator");
14const context_id_factory_1 = require("../helpers/context-id-factory");
15const execution_context_host_1 = require("../helpers/execution-context-host");
16const messages_1 = require("../helpers/messages");
17const router_method_factory_1 = require("../helpers/router-method-factory");
18const constants_2 = require("../injector/constants");
19const interceptors_consumer_1 = require("../interceptors/interceptors-consumer");
20const interceptors_context_creator_1 = require("../interceptors/interceptors-context-creator");
21const pipes_consumer_1 = require("../pipes/pipes-consumer");
22const pipes_context_creator_1 = require("../pipes/pipes-context-creator");
23const request_constants_1 = require("./request/request-constants");
24const route_params_factory_1 = require("./route-params-factory");
25const router_execution_context_1 = require("./router-execution-context");
26class RouterExplorer {
27 constructor(metadataScanner, container, injector, routerProxy, exceptionsFilter, config, routePathFactory) {
28 this.metadataScanner = metadataScanner;
29 this.container = container;
30 this.injector = injector;
31 this.routerProxy = routerProxy;
32 this.exceptionsFilter = exceptionsFilter;
33 this.config = config;
34 this.routePathFactory = routePathFactory;
35 this.routerMethodFactory = new router_method_factory_1.RouterMethodFactory();
36 this.logger = new logger_service_1.Logger(RouterExplorer.name, {
37 timestamp: true,
38 });
39 this.exceptionFiltersCache = new WeakMap();
40 const routeParamsFactory = new route_params_factory_1.RouteParamsFactory();
41 const pipesContextCreator = new pipes_context_creator_1.PipesContextCreator(container, config);
42 const pipesConsumer = new pipes_consumer_1.PipesConsumer();
43 const guardsContextCreator = new guards_context_creator_1.GuardsContextCreator(container, config);
44 const guardsConsumer = new guards_consumer_1.GuardsConsumer();
45 const interceptorsContextCreator = new interceptors_context_creator_1.InterceptorsContextCreator(container, config);
46 const interceptorsConsumer = new interceptors_consumer_1.InterceptorsConsumer();
47 this.executionContextCreator = new router_execution_context_1.RouterExecutionContext(routeParamsFactory, pipesContextCreator, pipesConsumer, guardsContextCreator, guardsConsumer, interceptorsContextCreator, interceptorsConsumer, container.getHttpAdapterRef());
48 }
49 explore(instanceWrapper, moduleKey, applicationRef, host, routePathMetadata) {
50 const { instance } = instanceWrapper;
51 const routerPaths = this.scanForPaths(instance);
52 this.applyPathsToRouterProxy(applicationRef, routerPaths, instanceWrapper, moduleKey, routePathMetadata, host);
53 }
54 extractRouterPath(metatype) {
55 const path = Reflect.getMetadata(constants_1.PATH_METADATA, metatype);
56 if ((0, shared_utils_1.isUndefined)(path)) {
57 throw new unknown_request_mapping_exception_1.UnknownRequestMappingException();
58 }
59 if (Array.isArray(path)) {
60 return path.map(p => (0, shared_utils_1.addLeadingSlash)(p));
61 }
62 return [(0, shared_utils_1.addLeadingSlash)(path)];
63 }
64 scanForPaths(instance, prototype) {
65 const instancePrototype = (0, shared_utils_1.isUndefined)(prototype)
66 ? Object.getPrototypeOf(instance)
67 : prototype;
68 return this.metadataScanner.scanFromPrototype(instance, instancePrototype, method => this.exploreMethodMetadata(instance, instancePrototype, method));
69 }
70 exploreMethodMetadata(instance, prototype, methodName) {
71 const instanceCallback = instance[methodName];
72 const prototypeCallback = prototype[methodName];
73 const routePath = Reflect.getMetadata(constants_1.PATH_METADATA, prototypeCallback);
74 if ((0, shared_utils_1.isUndefined)(routePath)) {
75 return null;
76 }
77 const requestMethod = Reflect.getMetadata(constants_1.METHOD_METADATA, prototypeCallback);
78 const version = Reflect.getMetadata(constants_1.VERSION_METADATA, prototypeCallback);
79 const path = (0, shared_utils_1.isString)(routePath)
80 ? [(0, shared_utils_1.addLeadingSlash)(routePath)]
81 : routePath.map((p) => (0, shared_utils_1.addLeadingSlash)(p));
82 return {
83 path,
84 requestMethod,
85 targetCallback: instanceCallback,
86 methodName,
87 version,
88 };
89 }
90 applyPathsToRouterProxy(router, routeDefinitions, instanceWrapper, moduleKey, routePathMetadata, host) {
91 (routeDefinitions || []).forEach(routeDefinition => {
92 const { version: methodVersion } = routeDefinition;
93 routePathMetadata.methodVersion = methodVersion;
94 this.applyCallbackToRouter(router, routeDefinition, instanceWrapper, moduleKey, routePathMetadata, host);
95 });
96 }
97 applyCallbackToRouter(router, routeDefinition, instanceWrapper, moduleKey, routePathMetadata, host) {
98 const { path: paths, requestMethod, targetCallback, methodName, } = routeDefinition;
99 const { instance } = instanceWrapper;
100 const routerMethodRef = this.routerMethodFactory
101 .get(router, requestMethod)
102 .bind(router);
103 const isRequestScoped = !instanceWrapper.isDependencyTreeStatic();
104 const proxy = isRequestScoped
105 ? this.createRequestScopedHandler(instanceWrapper, requestMethod, this.container.getModuleByKey(moduleKey), moduleKey, methodName)
106 : this.createCallbackProxy(instance, targetCallback, methodName, moduleKey, requestMethod);
107 const isVersioned = (routePathMetadata.methodVersion ||
108 routePathMetadata.controllerVersion) &&
109 routePathMetadata.versioningOptions;
110 let routeHandler = this.applyHostFilter(host, proxy);
111 paths.forEach(path => {
112 if (isVersioned &&
113 routePathMetadata.versioningOptions.type !== version_type_enum_1.VersioningType.URI) {
114 // All versioning (except for URI Versioning) is done via the "Version Filter"
115 routeHandler = this.applyVersionFilter(router, routePathMetadata, routeHandler);
116 }
117 routePathMetadata.methodPath = path;
118 const pathsToRegister = this.routePathFactory.create(routePathMetadata, requestMethod);
119 pathsToRegister.forEach(path => routerMethodRef(path, routeHandler));
120 const pathsToLog = this.routePathFactory.create(Object.assign(Object.assign({}, routePathMetadata), { versioningOptions: undefined }), requestMethod);
121 pathsToLog.forEach(path => {
122 if (isVersioned) {
123 const version = this.routePathFactory.getVersion(routePathMetadata);
124 this.logger.log((0, messages_1.VERSIONED_ROUTE_MAPPED_MESSAGE)(path, requestMethod, version));
125 }
126 else {
127 this.logger.log((0, messages_1.ROUTE_MAPPED_MESSAGE)(path, requestMethod));
128 }
129 });
130 });
131 }
132 applyHostFilter(host, handler) {
133 if (!host) {
134 return handler;
135 }
136 const httpAdapterRef = this.container.getHttpAdapterRef();
137 const hosts = Array.isArray(host) ? host : [host];
138 const hostRegExps = hosts.map((host) => {
139 const keys = [];
140 const regexp = pathToRegexp(host, keys);
141 return { regexp, keys };
142 });
143 const unsupportedFilteringErrorMessage = Array.isArray(host)
144 ? `HTTP adapter does not support filtering on hosts: ["${host.join('", "')}"]`
145 : `HTTP adapter does not support filtering on host: "${host}"`;
146 return (req, res, next) => {
147 req.hosts = {};
148 const hostname = httpAdapterRef.getRequestHostname(req) || '';
149 for (const exp of hostRegExps) {
150 const match = hostname.match(exp.regexp);
151 if (match) {
152 exp.keys.forEach((key, i) => (req.hosts[key.name] = match[i + 1]));
153 return handler(req, res, next);
154 }
155 }
156 if (!next) {
157 throw new exceptions_1.InternalServerErrorException(unsupportedFilteringErrorMessage);
158 }
159 return next();
160 };
161 }
162 applyVersionFilter(router, routePathMetadata, handler) {
163 const { versioningOptions } = routePathMetadata;
164 const version = this.routePathFactory.getVersion(routePathMetadata);
165 if (router === null || router === void 0 ? void 0 : router.applyVersionFilter) {
166 return router.applyVersionFilter(handler, version, versioningOptions);
167 }
168 /**
169 * TODO(v9): This was left for backward-compatibility and can be removed.
170 */
171 return (req, res, next) => {
172 var _a, _b, _c, _d;
173 if (version === version_options_interface_1.VERSION_NEUTRAL) {
174 return handler(req, res, next);
175 }
176 // URL Versioning is done via the path, so the filter continues forward
177 if (versioningOptions.type === version_type_enum_1.VersioningType.URI) {
178 return handler(req, res, next);
179 }
180 // Custom Extractor Versioning Handler
181 if (versioningOptions.type === version_type_enum_1.VersioningType.CUSTOM) {
182 const extractedVersion = versioningOptions.extractor(req);
183 if (Array.isArray(version)) {
184 if (Array.isArray(extractedVersion) &&
185 version.filter(extractedVersion.includes).length) {
186 return handler(req, res, next);
187 }
188 else if ((0, shared_utils_1.isString)(extractedVersion) &&
189 version.includes(extractedVersion)) {
190 return handler(req, res, next);
191 }
192 }
193 else {
194 if (Array.isArray(extractedVersion) &&
195 extractedVersion.includes(version)) {
196 return handler(req, res, next);
197 }
198 else if ((0, shared_utils_1.isString)(extractedVersion) &&
199 version === extractedVersion) {
200 return handler(req, res, next);
201 }
202 }
203 }
204 // Media Type (Accept Header) Versioning Handler
205 if (versioningOptions.type === version_type_enum_1.VersioningType.MEDIA_TYPE) {
206 const MEDIA_TYPE_HEADER = 'Accept';
207 const acceptHeaderValue = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a[MEDIA_TYPE_HEADER]) ||
208 ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[MEDIA_TYPE_HEADER.toLowerCase()]);
209 const acceptHeaderVersionParameter = acceptHeaderValue
210 ? acceptHeaderValue.split(';')[1]
211 : undefined;
212 // No version was supplied
213 if ((0, shared_utils_1.isUndefined)(acceptHeaderVersionParameter)) {
214 if (Array.isArray(version)) {
215 if (version.includes(version_options_interface_1.VERSION_NEUTRAL)) {
216 return handler(req, res, next);
217 }
218 }
219 }
220 else {
221 const headerVersion = acceptHeaderVersionParameter.split(versioningOptions.key)[1];
222 if (Array.isArray(version)) {
223 if (version.includes(headerVersion)) {
224 return handler(req, res, next);
225 }
226 }
227 else if ((0, shared_utils_1.isString)(version)) {
228 if (version === headerVersion) {
229 return handler(req, res, next);
230 }
231 }
232 }
233 }
234 // Header Versioning Handler
235 else if (versioningOptions.type === version_type_enum_1.VersioningType.HEADER) {
236 const customHeaderVersionParameter = ((_c = req.headers) === null || _c === void 0 ? void 0 : _c[versioningOptions.header]) ||
237 ((_d = req.headers) === null || _d === void 0 ? void 0 : _d[versioningOptions.header.toLowerCase()]);
238 // No version was supplied
239 if ((0, shared_utils_1.isUndefined)(customHeaderVersionParameter)) {
240 if (Array.isArray(version)) {
241 if (version.includes(version_options_interface_1.VERSION_NEUTRAL)) {
242 return handler(req, res, next);
243 }
244 }
245 }
246 else {
247 if (Array.isArray(version)) {
248 if (version.includes(customHeaderVersionParameter)) {
249 return handler(req, res, next);
250 }
251 }
252 else if ((0, shared_utils_1.isString)(version)) {
253 if (version === customHeaderVersionParameter) {
254 return handler(req, res, next);
255 }
256 }
257 }
258 }
259 if (!next) {
260 throw new exceptions_1.InternalServerErrorException('HTTP adapter does not support filtering on version');
261 }
262 return next();
263 };
264 }
265 createCallbackProxy(instance, callback, methodName, moduleRef, requestMethod, contextId = constants_2.STATIC_CONTEXT, inquirerId) {
266 const executionContext = this.executionContextCreator.create(instance, callback, methodName, moduleRef, requestMethod, contextId, inquirerId);
267 const exceptionFilter = this.exceptionsFilter.create(instance, callback, moduleRef, contextId, inquirerId);
268 return this.routerProxy.createProxy(executionContext, exceptionFilter);
269 }
270 createRequestScopedHandler(instanceWrapper, requestMethod, moduleRef, moduleKey, methodName) {
271 const { instance } = instanceWrapper;
272 const collection = moduleRef.controllers;
273 return async (req, res, next) => {
274 try {
275 const contextId = this.getContextId(req);
276 const contextInstance = await this.injector.loadPerContext(instance, moduleRef, collection, contextId);
277 await this.createCallbackProxy(contextInstance, contextInstance[methodName], methodName, moduleKey, requestMethod, contextId, instanceWrapper.id)(req, res, next);
278 }
279 catch (err) {
280 let exceptionFilter = this.exceptionFiltersCache.get(instance[methodName]);
281 if (!exceptionFilter) {
282 exceptionFilter = this.exceptionsFilter.create(instance, instance[methodName], moduleKey);
283 this.exceptionFiltersCache.set(instance[methodName], exceptionFilter);
284 }
285 const host = new execution_context_host_1.ExecutionContextHost([req, res, next]);
286 exceptionFilter.next(err, host);
287 }
288 };
289 }
290 getContextId(request) {
291 const contextId = context_id_factory_1.ContextIdFactory.getByRequest(request);
292 if (!request[request_constants_1.REQUEST_CONTEXT_ID]) {
293 Object.defineProperty(request, request_constants_1.REQUEST_CONTEXT_ID, {
294 value: contextId,
295 enumerable: false,
296 writable: false,
297 configurable: false,
298 });
299 this.container.registerRequestProvider(request, contextId);
300 }
301 return contextId;
302 }
303}
304exports.RouterExplorer = RouterExplorer;