mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature() wip: DI scopes
This commit is contained in:
@@ -1,28 +1,47 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
'use strict';
|
||||
var __decorate =
|
||||
(this && this.__decorate) ||
|
||||
function(decorators, target, key, desc) {
|
||||
var c = arguments.length,
|
||||
r =
|
||||
c < 3
|
||||
? target
|
||||
: desc === null
|
||||
? (desc = Object.getOwnPropertyDescriptor(target, key))
|
||||
: desc,
|
||||
d;
|
||||
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
|
||||
r = Reflect.decorate(decorators, target, key, desc);
|
||||
else
|
||||
for (var i = decorators.length - 1; i >= 0; i--)
|
||||
if ((d = decorators[i]))
|
||||
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const common_1 = require("@nestjs/common");
|
||||
};
|
||||
var __metadata =
|
||||
(this && this.__metadata) ||
|
||||
function(k, v) {
|
||||
if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function')
|
||||
return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const common_1 = require('@nestjs/common');
|
||||
let AppController = class AppController {
|
||||
root() {
|
||||
return 'Hello world!';
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
__decorate(
|
||||
[
|
||||
common_1.Get(),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", String)
|
||||
], AppController.prototype, "root", null);
|
||||
AppController = __decorate([
|
||||
common_1.Controller()
|
||||
], AppController);
|
||||
__metadata('design:type', Function),
|
||||
__metadata('design:paramtypes', []),
|
||||
__metadata('design:returntype', String),
|
||||
],
|
||||
AppController.prototype,
|
||||
'root',
|
||||
null,
|
||||
);
|
||||
AppController = __decorate([common_1.Controller()], AppController);
|
||||
exports.AppController = AppController;
|
||||
//# sourceMappingURL=app.controller.js.map
|
||||
@@ -39,9 +39,12 @@ gulp.task('copy-misc', function() {
|
||||
|
||||
gulp.task('clean:output', function() {
|
||||
return gulp
|
||||
.src([`${source}/**/*.js`, `${source}/**/*.d.ts`], {
|
||||
.src(
|
||||
[`${source}/**/*.js`, `${source}/**/*.d.ts`, `${source}/**/*.js.map`],
|
||||
{
|
||||
read: false,
|
||||
})
|
||||
},
|
||||
)
|
||||
.pipe(clean());
|
||||
});
|
||||
|
||||
|
||||
34
package-lock.json
generated
34
package-lock.json
generated
@@ -111,20 +111,32 @@
|
||||
}
|
||||
},
|
||||
"@nestjs/common": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-5.1.0.tgz",
|
||||
"integrity": "sha512-JAZFqdU+f4DRE4yOvpfWDtwgmCavyfE2Vu7mSwYsklU9TlBBE9XBygN2J38aQC83dmCJ1H889shd+hBIiVyEXA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-5.5.0.tgz",
|
||||
"integrity": "sha512-Ifh1D4ypsJYs/3YBIocU+X5yuAZSVKuCsz8kaKB4ZUO5WwJjh4/x6hlr4A+9XUMe8fPLtYXVohJoRUU5HbwyIA==",
|
||||
"requires": {
|
||||
"axios": "0.17.1",
|
||||
"axios": "0.18.0",
|
||||
"cli-color": "1.2.0",
|
||||
"deprecate": "1.0.0",
|
||||
"multer": "1.3.0"
|
||||
"multer": "1.3.0",
|
||||
"uuid": "3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.3.0",
|
||||
"is-buffer": "^1.1.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nestjs/core": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-5.4.1.tgz",
|
||||
"integrity": "sha512-PAlSEsycXPIdVm1GOeghs0co8cUmMX65FPjzBLtRJKKcPS8YM7Xo3WKd2f2oGMACYNXbulLk8rWN0xTO2HTgww==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-5.5.0.tgz",
|
||||
"integrity": "sha512-XPUjSJyex6KMdTUKK1oeD7ea9mNLcwlSEbcKV7OWaNHIVq/XJaFpbzjbmd+/U/ZZaO1IWhpisfLW9gr/O8eb4w==",
|
||||
"requires": {
|
||||
"@nuxtjs/opencollective": "0.1.0",
|
||||
"body-parser": "1.18.3",
|
||||
@@ -586,9 +598,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "7.0.48",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.48.tgz",
|
||||
"integrity": "sha512-LLlXafM3BD52MH056tHxTXO8JFCnpJJQkdzIU3+m8ew+CXJY/5zIXgDNb4TK/QFvlI8QexLS5tL+sE0Qhegr1w=="
|
||||
"version": "10.12.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.14.tgz",
|
||||
"integrity": "sha512-0rVcFRhM93kRGAU88ASCjX9Y3FWDCh+33G5Z5evpKOea4xcpLqDGwmo64+DjgaSezTN5j9KdnUzvxhOw7fNciQ=="
|
||||
},
|
||||
"@types/pino": {
|
||||
"version": "4.7.1",
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.3.0",
|
||||
"@nestjs/common": "5.1.0",
|
||||
"@nestjs/core": "^5.3.10",
|
||||
"@nestjs/common": "^5.5.0",
|
||||
"@nestjs/core": "^5.5.0",
|
||||
"@nestjs/microservices": "5.1.0",
|
||||
"@nestjs/testing": "5.1.0",
|
||||
"@nestjs/websockets": "5.1.0",
|
||||
@@ -82,7 +82,7 @@
|
||||
"@types/express": "^4.0.39",
|
||||
"@types/kafka-node": "^2.0.6",
|
||||
"@types/mocha": "^2.2.38",
|
||||
"@types/node": "^7.0.5",
|
||||
"@types/node": "^10.12.14",
|
||||
"@types/redis": "^0.12.36",
|
||||
"@types/reflect-metadata": "0.0.5",
|
||||
"@types/sinon": "^1.16.36",
|
||||
|
||||
@@ -13,6 +13,7 @@ export const SELF_DECLARED_DEPS_METADATA = 'self:paramtypes';
|
||||
export const OPTIONAL_DEPS_METADATA = 'optional:paramtypes';
|
||||
export const PROPERTY_DEPS_METADATA = 'self:properties_metadata';
|
||||
export const OPTIONAL_PROPERTY_DEPS_METADATA = 'optional:properties_metadata';
|
||||
export const SCOPE_OPTIONS_METADATA = 'scope:options';
|
||||
|
||||
export const METHOD_METADATA = 'method';
|
||||
export const ROUTE_ARGS_METADATA = '__routeArguments__';
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
import { PATH_METADATA } from '../../constants';
|
||||
import { isUndefined } from '../../utils/shared.utils';
|
||||
import { PATH_METADATA, SCOPE_OPTIONS_METADATA } from '../../constants';
|
||||
import { isObject, isUndefined } from '../../utils/shared.utils';
|
||||
import { ScopeOptions } from './../../interfaces/scope-options.interface';
|
||||
|
||||
export interface ControllerOptions extends ScopeOptions {}
|
||||
|
||||
/**
|
||||
* Defines the controller. Controller can inject dependencies through constructor.
|
||||
* Those dependencies have to belong to the same module.
|
||||
*/
|
||||
export function Controller(prefix?: string): ClassDecorator {
|
||||
export function Controller(prefix?: string): ClassDecorator;
|
||||
export function Controller(options?: ControllerOptions): ClassDecorator;
|
||||
export function Controller(
|
||||
prefix?: string,
|
||||
options?: ControllerOptions,
|
||||
): ClassDecorator;
|
||||
export function Controller(
|
||||
prefixOrOptions?: string | ControllerOptions,
|
||||
options?: ControllerOptions,
|
||||
): ClassDecorator {
|
||||
const [prefix, controllerOptions] = isObject(prefixOrOptions)
|
||||
? [undefined, prefixOrOptions]
|
||||
: [prefixOrOptions, options];
|
||||
|
||||
const path = isUndefined(prefix) ? '/' : prefix;
|
||||
return (target: object) => {
|
||||
Reflect.defineMetadata(PATH_METADATA, path, target);
|
||||
Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, controllerOptions, target);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import * as uuid from 'uuid/v4';
|
||||
import { ScopeOptions } from '../../interfaces/scope-options.interface';
|
||||
import { SCOPE_OPTIONS_METADATA } from './../../constants';
|
||||
import { Type } from './../../interfaces/type.interface';
|
||||
|
||||
export interface InjectableOptions extends ScopeOptions {}
|
||||
|
||||
/**
|
||||
* Defines the injectable class. This class can inject dependencies through constructor.
|
||||
* Those dependencies have to belong to the same module.
|
||||
*/
|
||||
export function Injectable(): ClassDecorator {
|
||||
return (target: object) => {};
|
||||
export function Injectable(options?: InjectableOptions): ClassDecorator {
|
||||
return (target: object) => {
|
||||
Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, options, target);
|
||||
};
|
||||
}
|
||||
|
||||
export function mixin(mixinClass: Type<any>) {
|
||||
|
||||
@@ -39,6 +39,7 @@ export {
|
||||
PipeTransform,
|
||||
Provider,
|
||||
RpcExceptionFilter,
|
||||
Scope,
|
||||
Type,
|
||||
ValidationError,
|
||||
WebSocketAdapter,
|
||||
|
||||
@@ -29,5 +29,6 @@ export * from './nest-fastify-application.interface';
|
||||
export * from './nest-microservice.interface';
|
||||
export * from './on-application-bootstrap.interface';
|
||||
export * from './request-mapping-metadata.interface';
|
||||
export * from './scope-options.interface';
|
||||
export * from './type.interface';
|
||||
export * from './websockets/web-socket-adapter.interface';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Scope } from '../scope-options.interface';
|
||||
import { Type } from '../type.interface';
|
||||
|
||||
export type Provider =
|
||||
@@ -9,6 +10,7 @@ export type Provider =
|
||||
export interface ClassProvider {
|
||||
provide: any;
|
||||
useClass: Type<any>;
|
||||
scope?: Scope;
|
||||
}
|
||||
|
||||
export interface ValueProvider {
|
||||
@@ -20,4 +22,5 @@ export interface FactoryProvider {
|
||||
provide: any;
|
||||
useFactory: (...args: any[]) => any;
|
||||
inject?: Array<Type<any> | string | any>;
|
||||
scope?: Scope;
|
||||
}
|
||||
9
packages/common/interfaces/scope-options.interface.ts
Normal file
9
packages/common/interfaces/scope-options.interface.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export enum Scope {
|
||||
DEFAULT,
|
||||
REQUEST,
|
||||
LAZY,
|
||||
}
|
||||
|
||||
export interface ScopeOptions {
|
||||
scope?: Scope;
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
import { FILTER_CATCH_EXCEPTIONS } from '@nestjs/common/constants';
|
||||
import { Type } from '@nestjs/common/interfaces';
|
||||
import { ExceptionFilter } from '@nestjs/common/interfaces/exceptions/exception-filter.interface';
|
||||
import {
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
|
||||
|
||||
export class BaseExceptionFilterContext extends ContextCreator {
|
||||
protected moduleContext: string;
|
||||
@@ -19,6 +17,7 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
@@ -27,7 +26,7 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
.filter(
|
||||
instance => instance && (isFunction(instance.catch) || instance.name),
|
||||
)
|
||||
.map(filter => this.getFilterInstance(filter))
|
||||
.map(filter => this.getFilterInstance(filter, contextId))
|
||||
.map(instance => ({
|
||||
func: instance.catch.bind(instance),
|
||||
exceptionMetatypes: this.reflectCatchExceptions(instance),
|
||||
@@ -35,20 +34,25 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
.toArray() as R;
|
||||
}
|
||||
|
||||
public getFilterInstance(filter: Function | ExceptionFilter) {
|
||||
public getFilterInstance(
|
||||
filter: Function | ExceptionFilter,
|
||||
contextId: ContextId,
|
||||
) {
|
||||
const isObject = (filter as ExceptionFilter).catch;
|
||||
if (isObject) {
|
||||
return filter;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(filter);
|
||||
return instanceWrapper && instanceWrapper.instance
|
||||
? instanceWrapper.instance
|
||||
: null;
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype<T extends Record<string, any>>(
|
||||
filter: T,
|
||||
): { instance: any } | undefined {
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ import { CanActivate } from '@nestjs/common';
|
||||
import { GUARDS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { ConfigurationProvider } from '@nestjs/common/interfaces/configuration-provider.interface';
|
||||
import {
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
|
||||
|
||||
export class GuardsContextCreator extends ContextCreator {
|
||||
private moduleContext: string;
|
||||
@@ -25,6 +23,7 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
instance: Controller,
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): CanActivate[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(instance, callback, GUARDS_METADATA);
|
||||
@@ -32,31 +31,34 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
}
|
||||
return iterate(metadata)
|
||||
.filter((guard: any) => guard && (guard.name || guard.canActivate))
|
||||
.map(guard => this.getGuardInstance(guard))
|
||||
.map(guard => this.getGuardInstance(guard, contextId))
|
||||
.filter((guard: CanActivate) => guard && isFunction(guard.canActivate))
|
||||
.toArray() as R;
|
||||
}
|
||||
|
||||
public getGuardInstance(guard: Function | CanActivate) {
|
||||
public getGuardInstance(guard: Function | CanActivate, contextId: ContextId) {
|
||||
const isObject = (guard as CanActivate).canActivate;
|
||||
if (isObject) {
|
||||
return guard;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(guard);
|
||||
return instanceWrapper && instanceWrapper.instance
|
||||
? instanceWrapper.instance
|
||||
: null;
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype<T extends Record<string, any>>(
|
||||
guard: T,
|
||||
): { instance: any } | undefined {
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { ContextId } from '../injector/instance-wrapper';
|
||||
|
||||
export abstract class ContextCreator {
|
||||
public abstract createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId?: ContextId,
|
||||
): R;
|
||||
public getGlobalMetadata?<T extends any[]>(): T;
|
||||
|
||||
@@ -10,15 +13,19 @@ export abstract class ContextCreator {
|
||||
instance: Controller,
|
||||
callback: (...args: any[]) => any,
|
||||
metadataKey: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): R {
|
||||
const globalMetadata =
|
||||
this.getGlobalMetadata && this.getGlobalMetadata<T>();
|
||||
const classMetadata = this.reflectClassMetadata<T>(instance, metadataKey);
|
||||
const methodMetadata = this.reflectMethodMetadata<T>(callback, metadataKey);
|
||||
return [
|
||||
...this.createConcreteContext<T, R>(globalMetadata || ([] as T)),
|
||||
...this.createConcreteContext<T, R>(classMetadata),
|
||||
...this.createConcreteContext<T, R>(methodMetadata),
|
||||
...this.createConcreteContext<T, R>(
|
||||
globalMetadata || ([] as T),
|
||||
contextId,
|
||||
),
|
||||
...this.createConcreteContext<T, R>(classMetadata, contextId),
|
||||
...this.createConcreteContext<T, R>(methodMetadata, contextId),
|
||||
] as R;
|
||||
}
|
||||
|
||||
|
||||
63
packages/core/hooks/async-context.ts
Normal file
63
packages/core/hooks/async-context.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||
import * as asyncHooks from 'async_hooks';
|
||||
import { AsyncHooksHelper } from './async-hooks-helper';
|
||||
import { AsyncHooksStorage } from './async-hooks-storage';
|
||||
|
||||
export class AsyncContext implements OnModuleInit, OnModuleDestroy {
|
||||
private static _instance: AsyncContext;
|
||||
|
||||
private constructor(
|
||||
private readonly internalStorage: Map<number, any>,
|
||||
private readonly asyncHookRef: asyncHooks.AsyncHook,
|
||||
) {}
|
||||
|
||||
static get instance() {
|
||||
if (!this._instance) {
|
||||
this.initialize();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.asyncHookRef.enable();
|
||||
}
|
||||
|
||||
onModuleDestroy() {
|
||||
this.asyncHookRef.disable();
|
||||
}
|
||||
|
||||
set<TKey = any, TValue = any>(key: TKey, value: TValue) {
|
||||
const store = this.getAsyncStorage();
|
||||
store.set(key, value);
|
||||
}
|
||||
|
||||
get<TKey = any, TReturnValue = any>(key: TKey): TReturnValue {
|
||||
const store = this.getAsyncStorage();
|
||||
return store.get(key) as TReturnValue;
|
||||
}
|
||||
|
||||
run(fn: Function) {
|
||||
const eid = asyncHooks.executionAsyncId();
|
||||
this.internalStorage.set(eid, new Map());
|
||||
fn();
|
||||
}
|
||||
|
||||
private getAsyncStorage(): Map<unknown, unknown> {
|
||||
const eid = asyncHooks.executionAsyncId();
|
||||
const state = this.internalStorage.get(eid);
|
||||
if (!state) {
|
||||
throw new Error(
|
||||
`Async ID (${eid}) is not registered within internal cache.`,
|
||||
);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private static initialize() {
|
||||
const asyncHooksStorage = new AsyncHooksStorage();
|
||||
const asyncHook = AsyncHooksHelper.createHooks(asyncHooksStorage);
|
||||
const storage = asyncHooksStorage.getInternalStorage();
|
||||
|
||||
this._instance = new AsyncContext(storage, asyncHook);
|
||||
}
|
||||
}
|
||||
25
packages/core/hooks/async-hooks-helper.ts
Normal file
25
packages/core/hooks/async-hooks-helper.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import * as asyncHooks from 'async_hooks';
|
||||
import { AsyncHooksStorage } from './async-hooks-storage';
|
||||
|
||||
export class AsyncHooksHelper {
|
||||
static createHooks(storage: AsyncHooksStorage): asyncHooks.AsyncHook {
|
||||
function init(
|
||||
asyncId: number,
|
||||
type: string,
|
||||
triggerId: number,
|
||||
resource: Object,
|
||||
) {
|
||||
if (storage.has(triggerId)) {
|
||||
storage.inherit(asyncId, triggerId);
|
||||
}
|
||||
}
|
||||
function destroy(asyncId) {
|
||||
storage.delete(asyncId);
|
||||
}
|
||||
|
||||
return asyncHooks.createHook({
|
||||
init,
|
||||
destroy,
|
||||
} as asyncHooks.HookCallbacks);
|
||||
}
|
||||
}
|
||||
31
packages/core/hooks/async-hooks-storage.ts
Normal file
31
packages/core/hooks/async-hooks-storage.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export class AsyncHooksStorage {
|
||||
constructor(private readonly asyncStorage = new Map<number, unknown>()) {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
get<T = any>(triggerId: number): T {
|
||||
return this.asyncStorage.get(triggerId) as T;
|
||||
}
|
||||
|
||||
has(triggerId: number): boolean {
|
||||
return this.asyncStorage.has(triggerId);
|
||||
}
|
||||
|
||||
inherit(asyncId: number, triggerId: number) {
|
||||
const value = this.asyncStorage.get(triggerId);
|
||||
this.asyncStorage.set(asyncId, value);
|
||||
}
|
||||
|
||||
delete(asyncId: number) {
|
||||
this.asyncStorage.delete(asyncId);
|
||||
}
|
||||
|
||||
getInternalStorage(): Map<number, unknown> {
|
||||
return this.asyncStorage;
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
const initialAsyncId = 1;
|
||||
this.asyncStorage.set(initialAsyncId, new Map());
|
||||
}
|
||||
}
|
||||
1
packages/core/hooks/index.ts
Normal file
1
packages/core/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './async-context';
|
||||
@@ -10,6 +10,7 @@ export * from './adapters';
|
||||
export { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants';
|
||||
export { BaseExceptionFilter } from './exceptions/base-exception-filter';
|
||||
export { ApplicationReferenceHost } from './helpers/application-ref-host';
|
||||
export * from './hooks';
|
||||
export { ModuleRef } from './injector/module-ref';
|
||||
export { HTTP_SERVER_REF } from './injector/tokens';
|
||||
export { MiddlewareBuilder } from './middleware/builder';
|
||||
|
||||
6
packages/core/injector/constants.ts
Normal file
6
packages/core/injector/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ContextId } from './instance-wrapper';
|
||||
|
||||
const STATIC_CONTEXT_ID = 1;
|
||||
export const STATIC_CONTEXT: ContextId = Object.freeze({
|
||||
id: STATIC_CONTEXT_ID,
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
|
||||
import { InstanceWrapper, NestContainer } from './container';
|
||||
import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
export class ContainerScanner {
|
||||
@@ -35,7 +36,7 @@ export class ContainerScanner {
|
||||
if (!instanceWrapper) {
|
||||
throw new UnknownElementException();
|
||||
}
|
||||
return (instanceWrapper as InstanceWrapper<any>).instance;
|
||||
return (instanceWrapper as InstanceWrapper).instance;
|
||||
}
|
||||
|
||||
private initFlatContainer(): void {
|
||||
|
||||
@@ -101,7 +101,7 @@ export class NestContainer {
|
||||
return this.modules;
|
||||
}
|
||||
|
||||
public async addRelatedModule(
|
||||
public async addImport(
|
||||
relatedModule: Type<any> | DynamicModule,
|
||||
token: string,
|
||||
) {
|
||||
@@ -165,20 +165,17 @@ export class NestContainer {
|
||||
}
|
||||
|
||||
public bindGlobalScope() {
|
||||
this.modules.forEach(module => this.bindGlobalsToRelatedModules(module));
|
||||
this.modules.forEach(module => this.bindGlobalsToImports(module));
|
||||
}
|
||||
|
||||
public bindGlobalsToRelatedModules(module: Module) {
|
||||
public bindGlobalsToImports(module: Module) {
|
||||
this.globalModules.forEach(globalModule =>
|
||||
this.bindGlobalModuleToModule(module, globalModule),
|
||||
);
|
||||
}
|
||||
|
||||
public bindGlobalModuleToModule(module: Module, globalModule: Module) {
|
||||
if (module === globalModule) {
|
||||
return;
|
||||
}
|
||||
module.addRelatedModule(globalModule);
|
||||
public bindGlobalModuleToModule(target: Module, globalModule: Module) {
|
||||
target !== globalModule && target.addRelatedModule(globalModule);
|
||||
}
|
||||
|
||||
public getDynamicMetadataByToken(
|
||||
@@ -214,16 +211,3 @@ export class NestContainer {
|
||||
return this.modulesContainer;
|
||||
}
|
||||
}
|
||||
|
||||
export interface InstanceWrapper<T> {
|
||||
name: any;
|
||||
metatype: Type<T>;
|
||||
instance: T;
|
||||
isResolved: boolean;
|
||||
isPending?: boolean;
|
||||
done$?: Promise<void>;
|
||||
inject?: Type<any>[];
|
||||
isNotMetatype?: boolean;
|
||||
forwardRef?: boolean;
|
||||
async?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import {
|
||||
OPTIONAL_DEPS_METADATA,
|
||||
OPTIONAL_PROPERTY_DEPS_METADATA,
|
||||
@@ -17,14 +18,20 @@ import {
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception';
|
||||
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception';
|
||||
import { MiddlewareWrapper } from '../middleware/container';
|
||||
import { InstanceWrapper } from './container';
|
||||
import { AsyncContext } from './../hooks';
|
||||
import { STATIC_CONTEXT } from './constants';
|
||||
import {
|
||||
ContextId,
|
||||
InstancePerContext,
|
||||
InstanceWrapper,
|
||||
} from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
import { ModulesContainer } from './modules-container';
|
||||
|
||||
/**
|
||||
* The type of an injectable dependency
|
||||
*/
|
||||
export type InjectorDependency = Type<any> | Function | string;
|
||||
export type InjectorDependency = Type<any> | Function | string | symbol;
|
||||
|
||||
/**
|
||||
* The property-based dependency
|
||||
@@ -61,73 +68,100 @@ export interface InjectorDependencyContext {
|
||||
}
|
||||
|
||||
export class Injector {
|
||||
public async loadInstanceOfMiddleware(
|
||||
wrapper: MiddlewareWrapper,
|
||||
collection: Map<string, MiddlewareWrapper>,
|
||||
public async loadMiddleware(
|
||||
wrapper: InstanceWrapper,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const { metatype } = wrapper;
|
||||
const currentMetatype = collection.get(metatype.name);
|
||||
if (currentMetatype.instance !== null) {
|
||||
const targetMetatype = collection.get(metatype.name);
|
||||
if (targetMetatype.instance !== null) {
|
||||
return;
|
||||
}
|
||||
await this.resolveConstructorParams(
|
||||
wrapper as any,
|
||||
module,
|
||||
null,
|
||||
instances => {
|
||||
collection.set(metatype.name, {
|
||||
const loadInstance = (instances: any[]) => {
|
||||
const instanceWrapper = new InstanceWrapper({
|
||||
instance: new metatype(...instances),
|
||||
metatype,
|
||||
host: module,
|
||||
});
|
||||
},
|
||||
collection.set(metatype.name, instanceWrapper);
|
||||
};
|
||||
await this.resolveConstructorParams(
|
||||
wrapper,
|
||||
module,
|
||||
null,
|
||||
loadInstance,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
public async loadInstanceOfController(
|
||||
public async loadController(
|
||||
wrapper: InstanceWrapper<Controller>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const controllers = module.controllers;
|
||||
await this.loadInstance<Controller>(wrapper, controllers, module);
|
||||
await this.loadInstance<Controller>(
|
||||
wrapper,
|
||||
controllers,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
public async loadInstanceOfInjectable(
|
||||
public async loadInjectable(
|
||||
wrapper: InstanceWrapper<Controller>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const injectables = module.injectables;
|
||||
await this.loadInstance<Controller>(wrapper, injectables, module);
|
||||
await this.loadInstance<Controller>(
|
||||
wrapper,
|
||||
injectables,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
public loadPrototypeOfInstance<T>(
|
||||
public async loadProvider(
|
||||
wrapper: InstanceWrapper<Injectable>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const providers = module.providers;
|
||||
await this.loadInstance<Injectable>(wrapper, providers, module, contextId);
|
||||
}
|
||||
|
||||
public loadPrototype<T>(
|
||||
{ metatype, name }: InstanceWrapper<T>,
|
||||
collection: Map<string, InstanceWrapper<T>>,
|
||||
): void {
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
if (!collection) {
|
||||
return null;
|
||||
}
|
||||
const target = collection.get(name);
|
||||
if (target.isResolved || !isNil(target.inject) || !metatype.prototype) {
|
||||
const instanceHost = target.getInstanceByContextId(contextId);
|
||||
if (
|
||||
instanceHost.isResolved ||
|
||||
!isNil(target.inject) ||
|
||||
!metatype.prototype
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
collection.set(name, {
|
||||
...collection.get(name),
|
||||
collection.set(
|
||||
name,
|
||||
new InstanceWrapper({
|
||||
...target,
|
||||
instance: Object.create(metatype.prototype),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async loadInstanceOfComponent(
|
||||
wrapper: InstanceWrapper<Injectable>,
|
||||
module: Module,
|
||||
) {
|
||||
const providers = module.providers;
|
||||
await this.loadInstance<Injectable>(wrapper, providers, module);
|
||||
}
|
||||
|
||||
public applyDoneHook<T>(wrapper: InstanceWrapper<T>): () => void {
|
||||
public applyDoneHook<T>(wrapper: InstancePerContext<T>): () => void {
|
||||
let done: () => void;
|
||||
wrapper.done$ = new Promise<void>((resolve, reject) => {
|
||||
wrapper.donePromise = new Promise<void>((resolve, reject) => {
|
||||
done = resolve;
|
||||
});
|
||||
wrapper.isPending = true;
|
||||
@@ -136,33 +170,48 @@ export class Injector {
|
||||
|
||||
public async loadInstance<T>(
|
||||
wrapper: InstanceWrapper<T>,
|
||||
collection: Map<string, InstanceWrapper<any>>,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
if (wrapper.isPending) {
|
||||
return wrapper.done$;
|
||||
const instanceHost = wrapper.getInstanceByContextId(contextId);
|
||||
if (instanceHost.isPending) {
|
||||
return instanceHost.donePromise;
|
||||
}
|
||||
const done = this.applyDoneHook(wrapper);
|
||||
const done = this.applyDoneHook(instanceHost);
|
||||
const { name, inject } = wrapper;
|
||||
|
||||
const targetWrapper = collection.get(name);
|
||||
if (isUndefined(targetWrapper)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (targetWrapper.isResolved) {
|
||||
if (instanceHost.isResolved) {
|
||||
return;
|
||||
}
|
||||
const callback = async (instances: any[]) => {
|
||||
const properties = await this.resolveProperties(wrapper, module, inject);
|
||||
const properties = await this.resolveProperties(
|
||||
wrapper,
|
||||
module,
|
||||
inject,
|
||||
contextId,
|
||||
);
|
||||
const instance = await this.instantiateClass(
|
||||
instances,
|
||||
wrapper,
|
||||
targetWrapper,
|
||||
module.id,
|
||||
contextId,
|
||||
);
|
||||
this.applyProperties(instance, properties);
|
||||
done();
|
||||
};
|
||||
await this.resolveConstructorParams<T>(wrapper, module, inject, callback);
|
||||
await this.resolveConstructorParams<T>(
|
||||
wrapper,
|
||||
module,
|
||||
inject,
|
||||
callback,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
public async resolveConstructorParams<T>(
|
||||
@@ -170,7 +219,21 @@ export class Injector {
|
||||
module: Module,
|
||||
inject: InjectorDependency[],
|
||||
callback: (args: any[]) => void,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const metadata = wrapper.getCtorMetadata();
|
||||
if (metadata) {
|
||||
const dependenciesHosts = await Promise.all(
|
||||
metadata.map(async item =>
|
||||
this.resolveComponentHost(item.host, item, contextId),
|
||||
),
|
||||
);
|
||||
const deps = dependenciesHosts.map(
|
||||
item => item.getInstanceByContextId(contextId).instance,
|
||||
);
|
||||
return callback(deps);
|
||||
}
|
||||
|
||||
const dependencies = isNil(inject)
|
||||
? this.reflectConstructorParams(wrapper.metatype)
|
||||
: inject;
|
||||
@@ -179,19 +242,21 @@ export class Injector {
|
||||
: [];
|
||||
|
||||
let isResolved = true;
|
||||
const instances = await Promise.all(
|
||||
dependencies.map(async (param, index) => {
|
||||
|
||||
const findOneParam = async (param, index) => {
|
||||
try {
|
||||
const paramWrapper = await this.resolveSingleParam<T>(
|
||||
wrapper,
|
||||
param,
|
||||
{ index, dependencies },
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
if (!paramWrapper.isResolved && !paramWrapper.forwardRef) {
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(contextId);
|
||||
if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
|
||||
isResolved = false;
|
||||
}
|
||||
return paramWrapper.instance;
|
||||
return instanceHost && instanceHost.instance;
|
||||
} catch (err) {
|
||||
const isOptional = optionalDependenciesIds.includes(index);
|
||||
if (!isOptional) {
|
||||
@@ -199,8 +264,8 @@ export class Injector {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}),
|
||||
);
|
||||
};
|
||||
const instances = await Promise.all(dependencies.map(findOneParam));
|
||||
isResolved && (await callback(instances));
|
||||
}
|
||||
|
||||
@@ -225,6 +290,7 @@ export class Injector {
|
||||
param: Type<any> | string | symbol | any,
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
if (isUndefined(param)) {
|
||||
throw new UndefinedDependencyException(
|
||||
@@ -239,6 +305,7 @@ export class Injector {
|
||||
isFunction(token) ? (token as Type<any>).name : token,
|
||||
dependencyContext,
|
||||
wrapper,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,32 +325,53 @@ export class Injector {
|
||||
name: any,
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
) {
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<InstanceWrapper> {
|
||||
const providers = module.providers;
|
||||
const instanceWrapper = await this.lookupComponent(
|
||||
providers,
|
||||
module,
|
||||
{ ...dependencyContext, name },
|
||||
wrapper,
|
||||
contextId,
|
||||
);
|
||||
if (!instanceWrapper.isResolved && !instanceWrapper.forwardRef) {
|
||||
await this.loadInstanceOfComponent(instanceWrapper, module);
|
||||
return this.resolveComponentHost(module, instanceWrapper, contextId);
|
||||
}
|
||||
|
||||
public async resolveComponentHost<T>(
|
||||
module: Module,
|
||||
instanceWrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<InstanceWrapper> {
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
|
||||
await this.loadProvider(instanceWrapper, module, contextId);
|
||||
}
|
||||
if (instanceWrapper.async) {
|
||||
instanceWrapper.instance = await instanceWrapper.instance;
|
||||
instanceWrapper.setInstanceByContextId(
|
||||
contextId,
|
||||
await instanceWrapper.getInstanceByContextId(contextId),
|
||||
);
|
||||
}
|
||||
return instanceWrapper;
|
||||
}
|
||||
|
||||
public async lookupComponent<T = any>(
|
||||
providers: Map<string, any>,
|
||||
providers: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
) {
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<InstanceWrapper<T>> {
|
||||
const { name } = dependencyContext;
|
||||
const scanInExports = () =>
|
||||
this.lookupComponentInExports(dependencyContext, module, wrapper);
|
||||
this.lookupComponentInExports(
|
||||
dependencyContext,
|
||||
module,
|
||||
wrapper,
|
||||
contextId,
|
||||
);
|
||||
|
||||
return providers.has(name) ? providers.get(name) : scanInExports();
|
||||
}
|
||||
|
||||
@@ -291,10 +379,14 @@ export class Injector {
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
module: Module,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const instanceWrapper = await this.lookupComponentInRelatedModules(
|
||||
const instanceWrapper = await this.lookupComponentInImports(
|
||||
module,
|
||||
dependencyContext.name,
|
||||
[],
|
||||
wrapper,
|
||||
contextId,
|
||||
);
|
||||
if (isNil(instanceWrapper)) {
|
||||
throw new UnknownDependenciesException(
|
||||
@@ -306,15 +398,18 @@ export class Injector {
|
||||
return instanceWrapper;
|
||||
}
|
||||
|
||||
public async lookupComponentInRelatedModules(
|
||||
public async lookupComponentInImports(
|
||||
module: Module,
|
||||
name: any,
|
||||
moduleRegistry: any[] = [],
|
||||
wrapper: InstanceWrapper,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<any> {
|
||||
let componentRef = null;
|
||||
let instanceWrapperRef: InstanceWrapper = null;
|
||||
|
||||
const imports: Set<Module> = module.imports || new Set();
|
||||
const children = [...imports.values()].filter(item => item);
|
||||
|
||||
const relatedModules: Set<Module> = module.relatedModules || new Set();
|
||||
const children = [...relatedModules.values()].filter(item => item);
|
||||
for (const relatedModule of children) {
|
||||
if (moduleRegistry.includes(relatedModule.id)) {
|
||||
continue;
|
||||
@@ -322,33 +417,52 @@ export class Injector {
|
||||
moduleRegistry.push(relatedModule.id);
|
||||
const { providers, exports } = relatedModule;
|
||||
if (!exports.has(name) || !providers.has(name)) {
|
||||
const instanceRef = await this.lookupComponentInRelatedModules(
|
||||
const instanceRef = await this.lookupComponentInImports(
|
||||
relatedModule,
|
||||
name,
|
||||
moduleRegistry,
|
||||
wrapper,
|
||||
contextId,
|
||||
);
|
||||
if (instanceRef) {
|
||||
return instanceRef;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
componentRef = providers.get(name);
|
||||
if (!componentRef.isResolved && !componentRef.forwardRef) {
|
||||
await this.loadInstanceOfComponent(componentRef, relatedModule);
|
||||
instanceWrapperRef = providers.get(name);
|
||||
|
||||
const instanceHost = instanceWrapperRef.getInstanceByContextId(contextId);
|
||||
if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
|
||||
await this.loadProvider(instanceWrapperRef, relatedModule, contextId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return componentRef;
|
||||
return instanceWrapperRef;
|
||||
}
|
||||
|
||||
public async resolveProperties<T>(
|
||||
wrapper: InstanceWrapper<T>,
|
||||
module: Module,
|
||||
inject?: InjectorDependency[],
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<PropertyDependency[]> {
|
||||
if (!isNil(inject)) {
|
||||
return [];
|
||||
}
|
||||
const metadata = wrapper.getPropertiesMetadata();
|
||||
if (metadata) {
|
||||
const dependenciesHosts = await Promise.all(
|
||||
metadata.map(async ({ wrapper: item, key }) => ({
|
||||
key,
|
||||
host: await this.resolveComponentHost(item.host, item, contextId),
|
||||
})),
|
||||
);
|
||||
return dependenciesHosts.map(({ key, host }) => ({
|
||||
key,
|
||||
name: key,
|
||||
instance: host.getInstanceByContextId(contextId).instance,
|
||||
}));
|
||||
}
|
||||
const properties = this.reflectProperties(wrapper.metatype);
|
||||
const instances = await Promise.all(
|
||||
properties.map(async (item: PropertyDependency) => {
|
||||
@@ -362,8 +476,13 @@ export class Injector {
|
||||
item.name,
|
||||
dependencyContext,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
return (paramWrapper && paramWrapper.instance) || undefined;
|
||||
if (!paramWrapper) {
|
||||
return undefined;
|
||||
}
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(contextId);
|
||||
return instanceHost.instance;
|
||||
} catch (err) {
|
||||
if (!item.isOptional) {
|
||||
throw err;
|
||||
@@ -404,21 +523,90 @@ export class Injector {
|
||||
|
||||
public async instantiateClass<T = any>(
|
||||
instances: any[],
|
||||
wrapper: InstanceWrapper<any>,
|
||||
targetMetatype: InstanceWrapper<any>,
|
||||
wrapper: InstanceWrapper,
|
||||
targetMetatype: InstanceWrapper,
|
||||
moduleId: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Promise<T> {
|
||||
const { metatype, inject } = wrapper;
|
||||
const { metatype, inject, name, scope } = wrapper;
|
||||
const instanceHost = targetMetatype.getInstanceByContextId(contextId);
|
||||
const instanceKey = moduleId + name;
|
||||
|
||||
if (isNil(inject)) {
|
||||
targetMetatype.instance = wrapper.forwardRef
|
||||
? Object.assign(targetMetatype.instance, new metatype(...instances))
|
||||
const targetInstance = wrapper.getInstanceByContextId(contextId);
|
||||
targetInstance.instance = wrapper.forwardRef
|
||||
? Object.assign(targetInstance.instance, new metatype(...instances))
|
||||
: new metatype(...instances);
|
||||
|
||||
if (scope === Scope.LAZY) {
|
||||
this.mergeAsyncProxy(
|
||||
instances,
|
||||
targetInstance,
|
||||
instanceKey,
|
||||
metatype,
|
||||
(...args: any[]) => new metatype(...args),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const factoryResult = ((targetMetatype.metatype as any) as Function)(
|
||||
const factoryReturnValue = ((targetMetatype.metatype as any) as Function)(
|
||||
...instances,
|
||||
);
|
||||
targetMetatype.instance = await factoryResult;
|
||||
instanceHost.instance = await factoryReturnValue;
|
||||
if (scope === Scope.LAZY) {
|
||||
this.mergeAsyncProxy(
|
||||
instances,
|
||||
instanceHost,
|
||||
instanceKey,
|
||||
metatype,
|
||||
(...args: any[]) =>
|
||||
((targetMetatype.metatype as any) as Function)(...args),
|
||||
);
|
||||
}
|
||||
targetMetatype.isResolved = true;
|
||||
return targetMetatype.instance;
|
||||
}
|
||||
instanceHost.isResolved = true;
|
||||
return instanceHost.instance;
|
||||
}
|
||||
|
||||
mergeAsyncProxy(
|
||||
instances: any[],
|
||||
targetInstance: InstancePerContext<any>,
|
||||
instanceKey: string,
|
||||
metatype: Type<any>,
|
||||
factory: (...intances: any[]) => any,
|
||||
) {
|
||||
AsyncContext.instance.set(instanceKey, targetInstance.instance);
|
||||
|
||||
const proxy = (target: unknown, property: string | number | symbol) => {
|
||||
if (!(property in (target as object))) {
|
||||
return;
|
||||
}
|
||||
const cachedInstance = AsyncContext.instance.get(instanceKey);
|
||||
if (cachedInstance) {
|
||||
return cachedInstance[property];
|
||||
}
|
||||
const value = factory(...instances);
|
||||
AsyncContext.instance.set(instanceKey, value);
|
||||
return value[property];
|
||||
};
|
||||
targetInstance.instance = new Proxy(targetInstance.instance, {
|
||||
get: proxy,
|
||||
set: proxy,
|
||||
});
|
||||
}
|
||||
|
||||
async loadControllerPerContext<T>(
|
||||
instance: Controller,
|
||||
modulesContainer: ModulesContainer,
|
||||
moduleKey: string,
|
||||
ctx: ContextId,
|
||||
): Promise<T> {
|
||||
const module = modulesContainer.get(moduleKey);
|
||||
|
||||
const { controllers } = module;
|
||||
const controller = controllers.get(instance.constructor.name);
|
||||
await this.loadInstance(controller, controllers, module, ctx);
|
||||
|
||||
const wrapper = controller.getInstanceByContextId(ctx);
|
||||
return wrapper && (wrapper.instance as T);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,52 +41,49 @@ export class InstanceLoader {
|
||||
}
|
||||
|
||||
private createPrototypesOfProviders(module: Module) {
|
||||
module.providers.forEach(wrapper => {
|
||||
this.injector.loadPrototypeOfInstance<Injectable>(
|
||||
wrapper,
|
||||
module.providers,
|
||||
const { providers } = module;
|
||||
providers.forEach(wrapper =>
|
||||
this.injector.loadPrototype<Injectable>(wrapper, providers),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async createInstancesOfProviders(module: Module) {
|
||||
const { providers } = module;
|
||||
await Promise.all(
|
||||
[...module.providers.values()].map(async wrapper =>
|
||||
this.injector.loadInstanceOfComponent(wrapper, module),
|
||||
[...providers.values()].map(async wrapper =>
|
||||
this.injector.loadProvider(wrapper, module),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private createPrototypesOfControllers(module: Module) {
|
||||
module.controllers.forEach(wrapper => {
|
||||
this.injector.loadPrototypeOfInstance<Controller>(
|
||||
wrapper,
|
||||
module.controllers,
|
||||
const { controllers } = module;
|
||||
controllers.forEach(wrapper =>
|
||||
this.injector.loadPrototype<Controller>(wrapper, controllers),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async createInstancesOfControllers(module: Module) {
|
||||
const { controllers } = module;
|
||||
await Promise.all(
|
||||
[...module.controllers.values()].map(async wrapper =>
|
||||
this.injector.loadInstanceOfController(wrapper, module),
|
||||
[...controllers.values()].map(async wrapper =>
|
||||
this.injector.loadController(wrapper, module),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private createPrototypesOfInjectables(module: Module) {
|
||||
module.injectables.forEach(wrapper => {
|
||||
this.injector.loadPrototypeOfInstance<Controller>(
|
||||
wrapper,
|
||||
module.injectables,
|
||||
const { injectables } = module;
|
||||
injectables.forEach(wrapper =>
|
||||
this.injector.loadPrototype<Controller>(wrapper, injectables),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async createInstancesOfInjectables(module: Module) {
|
||||
const { injectables } = module;
|
||||
await Promise.all(
|
||||
[...module.injectables.values()].map(async wrapper =>
|
||||
this.injector.loadInstanceOfInjectable(wrapper, module),
|
||||
[...injectables.values()].map(async wrapper =>
|
||||
this.injector.loadInjectable(wrapper, module),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
138
packages/core/injector/instance-wrapper.ts
Normal file
138
packages/core/injector/instance-wrapper.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { Scope, Type } from '@nestjs/common';
|
||||
import { STATIC_CONTEXT } from './constants';
|
||||
import { Module } from './module';
|
||||
|
||||
export const INSTANCE_METADATA_SYMBOL = Symbol.for('metadata:cache');
|
||||
|
||||
export interface ContextId {
|
||||
readonly id: number;
|
||||
}
|
||||
|
||||
export interface InstancePerContext<T> {
|
||||
instance: T;
|
||||
isResolved?: boolean;
|
||||
isPending?: boolean;
|
||||
donePromise?: Promise<void>;
|
||||
}
|
||||
export interface PropertyMetadata {
|
||||
key: string;
|
||||
wrapper: InstanceWrapper;
|
||||
}
|
||||
|
||||
export interface InstanceMetadataStore {
|
||||
dependencies?: InstanceWrapper[];
|
||||
properties?: PropertyMetadata[];
|
||||
}
|
||||
|
||||
export class InstanceWrapper<T = any> {
|
||||
public readonly name: any;
|
||||
public readonly metatype: Type<T>;
|
||||
public readonly inject?: (string | symbol | Function | Type<any>)[];
|
||||
public readonly async?: boolean;
|
||||
public readonly host?: Module;
|
||||
public readonly scope?: Scope = Scope.DEFAULT;
|
||||
public forwardRef?: boolean;
|
||||
|
||||
private readonly values = new WeakMap<ContextId, InstancePerContext<T>>();
|
||||
private readonly [INSTANCE_METADATA_SYMBOL]: InstanceMetadataStore = {};
|
||||
|
||||
constructor(
|
||||
metadata: Partial<InstanceWrapper<T>> & Partial<InstancePerContext<T>> = {},
|
||||
) {
|
||||
this.initialize(metadata);
|
||||
}
|
||||
|
||||
set instance(value: T) {
|
||||
this.values.set(STATIC_CONTEXT, { instance: value });
|
||||
}
|
||||
|
||||
get instance(): T {
|
||||
const instancePerContext = this.getInstanceByContextId(STATIC_CONTEXT);
|
||||
return instancePerContext.instance;
|
||||
}
|
||||
|
||||
get isNotMetatype(): boolean {
|
||||
return !!this.metatype;
|
||||
}
|
||||
|
||||
getInstanceByContextId(contextId: ContextId): InstancePerContext<T> {
|
||||
const instancePerContext = this.values.get(contextId);
|
||||
return instancePerContext
|
||||
? instancePerContext
|
||||
: this.cloneStaticInstance(contextId);
|
||||
}
|
||||
|
||||
setInstanceByContextId(contextId: ContextId, value: InstancePerContext<T>) {
|
||||
this.values.set(contextId, value);
|
||||
}
|
||||
|
||||
addCtorMetadata(index: number, wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].dependencies) {
|
||||
this[INSTANCE_METADATA_SYMBOL].dependencies = [];
|
||||
}
|
||||
this[INSTANCE_METADATA_SYMBOL].dependencies[index] = wrapper;
|
||||
}
|
||||
|
||||
getCtorMetadata(): InstanceWrapper[] {
|
||||
return this[INSTANCE_METADATA_SYMBOL].dependencies;
|
||||
}
|
||||
|
||||
addPropertiesMetadata(key: string, wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].properties) {
|
||||
this[INSTANCE_METADATA_SYMBOL].properties = [];
|
||||
}
|
||||
this[INSTANCE_METADATA_SYMBOL].properties.push({
|
||||
key,
|
||||
wrapper,
|
||||
});
|
||||
}
|
||||
|
||||
getPropertiesMetadata(): PropertyMetadata[] {
|
||||
return this[INSTANCE_METADATA_SYMBOL].properties;
|
||||
}
|
||||
|
||||
isDependencyTreeStatic(): boolean {
|
||||
if (this.scope === Scope.REQUEST) {
|
||||
return false;
|
||||
}
|
||||
const { dependencies, properties } = this[INSTANCE_METADATA_SYMBOL];
|
||||
|
||||
const isItemStatic = (item: InstanceWrapper) =>
|
||||
item.isDependencyTreeStatic();
|
||||
const isTreeStatic = (tree: InstanceWrapper[]) => tree.every(isItemStatic);
|
||||
|
||||
let isStatic =
|
||||
(dependencies && isTreeStatic(dependencies)) || !dependencies;
|
||||
isStatic =
|
||||
isStatic &&
|
||||
((properties && isTreeStatic(properties as any)) || !properties);
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
private initialize(
|
||||
metadata: Partial<InstanceWrapper<T>> & Partial<InstancePerContext<T>>,
|
||||
) {
|
||||
const { instance, isResolved, ...wrapperPartial } = metadata;
|
||||
Object.assign(this, wrapperPartial);
|
||||
|
||||
this.setInstanceByContextId(STATIC_CONTEXT, {
|
||||
instance,
|
||||
isResolved,
|
||||
});
|
||||
}
|
||||
|
||||
private cloneStaticInstance(contextId: ContextId): InstancePerContext<T> {
|
||||
const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
|
||||
if (this.isDependencyTreeStatic()) {
|
||||
return staticInstance;
|
||||
}
|
||||
const instancePerContext: InstancePerContext<T> = {
|
||||
...staticInstance,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
isPending: false,
|
||||
};
|
||||
this.setInstanceByContextId(contextId, instancePerContext);
|
||||
return instancePerContext;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { InstanceWrapper, NestContainer } from './container';
|
||||
import { NestContainer } from './container';
|
||||
import { ContainerScanner } from './container-scanner';
|
||||
import { Injector } from './injector';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
export abstract class ModuleRef {
|
||||
@@ -29,12 +30,13 @@ export abstract class ModuleRef {
|
||||
type: Type<T>,
|
||||
module: Module,
|
||||
): Promise<T> {
|
||||
const wrapper: InstanceWrapper<any> = {
|
||||
const wrapper = new InstanceWrapper({
|
||||
name: type.name,
|
||||
metatype: type,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
};
|
||||
host: module,
|
||||
});
|
||||
return new Promise<T>(async (resolve, reject) => {
|
||||
try {
|
||||
const callback = async (instances: any[]) => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DynamicModule } from '@nestjs/common';
|
||||
import { SHARED_MODULE_METADATA } from '@nestjs/common/constants';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import * as hash from 'object-hash';
|
||||
import stringify from 'fast-safe-stringify';
|
||||
import * as hash from 'object-hash';
|
||||
|
||||
export class ModuleTokenFactory {
|
||||
public create(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';
|
||||
import {
|
||||
Controller,
|
||||
DynamicModule,
|
||||
@@ -18,8 +20,10 @@ import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { UnknownExportException } from '../errors/exceptions/unknown-export.exception';
|
||||
import { ApplicationReferenceHost } from '../helpers/application-ref-host';
|
||||
import { ExternalContextCreator } from '../helpers/external-context-creator';
|
||||
import { AsyncContext } from '../hooks/async-context';
|
||||
import { Reflector } from '../services/reflector.service';
|
||||
import { InstanceWrapper, NestContainer } from './container';
|
||||
import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { ModuleRef } from './module-ref';
|
||||
import { ModulesContainer } from './modules-container';
|
||||
import { HTTP_SERVER_REF } from './tokens';
|
||||
@@ -28,11 +32,15 @@ export interface CustomProvider {
|
||||
provide: any;
|
||||
name: string;
|
||||
}
|
||||
export type OpaqueToken = string | symbol | object | Type<any>;
|
||||
export type CustomClass = CustomProvider & { useClass: Type<any> };
|
||||
export type OpaqueToken = string | symbol | Type<any>;
|
||||
export type CustomClass = CustomProvider & {
|
||||
useClass: Type<any>;
|
||||
scope?: Scope;
|
||||
};
|
||||
export type CustomFactory = CustomProvider & {
|
||||
useFactory: (...args: any[]) => any;
|
||||
inject?: OpaqueToken[];
|
||||
scope?: Scope;
|
||||
};
|
||||
export type CustomValue = CustomProvider & { useValue: any };
|
||||
export type ProviderMetatype =
|
||||
@@ -43,7 +51,7 @@ export type ProviderMetatype =
|
||||
|
||||
export class Module {
|
||||
private readonly _id: string;
|
||||
private readonly _relatedModules = new Set<Module>();
|
||||
private readonly _imports = new Set<Module>();
|
||||
private readonly _providers = new Map<any, InstanceWrapper<Injectable>>();
|
||||
private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>();
|
||||
private readonly _controllers = new Map<
|
||||
@@ -69,14 +77,21 @@ export class Module {
|
||||
return this._scope;
|
||||
}
|
||||
|
||||
get relatedModules(): Set<Module> {
|
||||
return this._relatedModules;
|
||||
}
|
||||
|
||||
get providers(): Map<string, InstanceWrapper<Injectable>> {
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
get imports(): Set<Module> {
|
||||
return this._imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left for backward-compatibility reasons
|
||||
*/
|
||||
get relatedModules(): Set<Module> {
|
||||
return this._imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left for backward-compatibility reasons
|
||||
*/
|
||||
@@ -123,96 +138,145 @@ export class Module {
|
||||
this.addExternalContextCreator(container.getExternalContextCreator());
|
||||
this.addModulesContainer(container.getModulesContainer());
|
||||
this.addApplicationRefHost(container.getApplicationRefHost());
|
||||
|
||||
this._providers.set(
|
||||
AsyncContext.name,
|
||||
new InstanceWrapper({
|
||||
name: AsyncContext.name,
|
||||
metatype: AsyncContext as any,
|
||||
isResolved: true,
|
||||
instance: AsyncContext.instance,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addModuleRef() {
|
||||
const moduleRef = this.createModuleRefMetatype();
|
||||
this._providers.set(ModuleRef.name, {
|
||||
const moduleRef = this.createModuleReferenceType();
|
||||
this._providers.set(
|
||||
ModuleRef.name,
|
||||
new InstanceWrapper({
|
||||
name: ModuleRef.name,
|
||||
metatype: ModuleRef as any,
|
||||
isResolved: true,
|
||||
instance: new moduleRef(),
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addModuleAsProvider() {
|
||||
this._providers.set(this._metatype.name, {
|
||||
this._providers.set(
|
||||
this._metatype.name,
|
||||
new InstanceWrapper({
|
||||
name: this._metatype.name,
|
||||
metatype: this._metatype,
|
||||
isResolved: false,
|
||||
instance: null,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addReflector(reflector: Reflector) {
|
||||
this._providers.set(Reflector.name, {
|
||||
this._providers.set(
|
||||
Reflector.name,
|
||||
new InstanceWrapper({
|
||||
name: Reflector.name,
|
||||
metatype: Reflector,
|
||||
isResolved: true,
|
||||
instance: reflector,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addApplicationRef(applicationRef: any) {
|
||||
this._providers.set(HTTP_SERVER_REF, {
|
||||
this._providers.set(
|
||||
HTTP_SERVER_REF,
|
||||
new InstanceWrapper({
|
||||
name: HTTP_SERVER_REF,
|
||||
metatype: {} as any,
|
||||
isResolved: true,
|
||||
instance: applicationRef || {},
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addExternalContextCreator(
|
||||
externalContextCreator: ExternalContextCreator,
|
||||
) {
|
||||
this._providers.set(ExternalContextCreator.name, {
|
||||
this._providers.set(
|
||||
ExternalContextCreator.name,
|
||||
new InstanceWrapper({
|
||||
name: ExternalContextCreator.name,
|
||||
metatype: ExternalContextCreator,
|
||||
isResolved: true,
|
||||
instance: externalContextCreator,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addModulesContainer(modulesContainer: ModulesContainer) {
|
||||
this._providers.set(ModulesContainer.name, {
|
||||
this._providers.set(
|
||||
ModulesContainer.name,
|
||||
new InstanceWrapper({
|
||||
name: ModulesContainer.name,
|
||||
metatype: ModulesContainer,
|
||||
isResolved: true,
|
||||
instance: modulesContainer,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addApplicationRefHost(applicationRefHost: ApplicationReferenceHost) {
|
||||
this._providers.set(ApplicationReferenceHost.name, {
|
||||
this._providers.set(
|
||||
ApplicationReferenceHost.name,
|
||||
new InstanceWrapper({
|
||||
name: ApplicationReferenceHost.name,
|
||||
metatype: ApplicationReferenceHost,
|
||||
isResolved: true,
|
||||
instance: applicationRefHost,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addInjectable<T = Injectable>(injectable: Type<T>) {
|
||||
if (this.isCustomProvider(injectable)) {
|
||||
return this.addCustomProvider(injectable, this._injectables);
|
||||
}
|
||||
this._injectables.set(injectable.name, {
|
||||
this._injectables.set(
|
||||
injectable.name,
|
||||
new InstanceWrapper({
|
||||
name: injectable.name,
|
||||
metatype: injectable,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
});
|
||||
scope: this.getClassScope(injectable),
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addProvider(provider: ProviderMetatype): string {
|
||||
if (this.isCustomProvider(provider)) {
|
||||
return this.addCustomProvider(provider, this._providers);
|
||||
}
|
||||
this._providers.set((provider as Type<Injectable>).name, {
|
||||
this._providers.set(
|
||||
(provider as Type<Injectable>).name,
|
||||
new InstanceWrapper({
|
||||
name: (provider as Type<Injectable>).name,
|
||||
metatype: provider as Type<Injectable>,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
});
|
||||
scope: this.getClassScope(provider),
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
return (provider as Type<Injectable>).name;
|
||||
}
|
||||
|
||||
@@ -228,17 +292,18 @@ export class Module {
|
||||
): string {
|
||||
const { provide } = provider;
|
||||
const name = isFunction(provide) ? provide.name : provide;
|
||||
const providerNamePair = {
|
||||
|
||||
provider = {
|
||||
...provider,
|
||||
name,
|
||||
};
|
||||
if (this.isCustomClass(providerNamePair))
|
||||
this.addCustomClass(providerNamePair, collection);
|
||||
else if (this.isCustomValue(providerNamePair))
|
||||
this.addCustomValue(providerNamePair, collection);
|
||||
else if (this.isCustomFactory(providerNamePair))
|
||||
this.addCustomFactory(providerNamePair, collection);
|
||||
|
||||
if (this.isCustomClass(provider)) {
|
||||
this.addCustomClass(provider, collection);
|
||||
} else if (this.isCustomValue(provider)) {
|
||||
this.addCustomValue(provider, collection);
|
||||
} else if (this.isCustomFactory(provider)) {
|
||||
this.addCustomFactory(provider, collection);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -258,41 +323,59 @@ export class Module {
|
||||
return exported && exported.module;
|
||||
}
|
||||
|
||||
public addCustomClass(provider: CustomClass, collection: Map<string, any>) {
|
||||
const { name, useClass } = provider;
|
||||
collection.set(name, {
|
||||
public addCustomClass(
|
||||
provider: CustomClass,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const { name, useClass, scope } = provider;
|
||||
collection.set(
|
||||
name,
|
||||
new InstanceWrapper({
|
||||
name,
|
||||
metatype: useClass,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
});
|
||||
scope,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addCustomValue(provider: CustomValue, collection: Map<string, any>) {
|
||||
public addCustomValue(
|
||||
provider: CustomValue,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const { name, useValue: value } = provider;
|
||||
collection.set(name, {
|
||||
collection.set(
|
||||
name,
|
||||
new InstanceWrapper({
|
||||
name,
|
||||
metatype: null,
|
||||
instance: value,
|
||||
isResolved: true,
|
||||
isNotMetatype: true,
|
||||
async: value instanceof Promise,
|
||||
});
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addCustomFactory(
|
||||
provider: CustomFactory,
|
||||
collection: Map<string, any>,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const { name, useFactory: factory, inject } = provider;
|
||||
collection.set(name, {
|
||||
const { name, useFactory: factory, inject, scope } = provider;
|
||||
collection.set(
|
||||
name,
|
||||
new InstanceWrapper({
|
||||
name,
|
||||
metatype: factory as any,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
inject: inject || [],
|
||||
isNotMetatype: true,
|
||||
});
|
||||
scope,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addExportedProvider(
|
||||
@@ -326,14 +409,14 @@ export class Module {
|
||||
if (this._providers.has(token)) {
|
||||
return token;
|
||||
}
|
||||
const importedArray = [...this._relatedModules.values()];
|
||||
const importedRefNames = importedArray
|
||||
const importsArray = [...this._imports.values()];
|
||||
const importsNames = importsArray
|
||||
.filter(item => item)
|
||||
.map(({ metatype }) => metatype)
|
||||
.filter(metatype => metatype)
|
||||
.map(({ name }) => name);
|
||||
|
||||
if (!importedRefNames.includes(token as any)) {
|
||||
if (!importsNames.includes(token as any)) {
|
||||
const { name } = this.metatype;
|
||||
throw new UnknownExportException(name);
|
||||
}
|
||||
@@ -341,16 +424,21 @@ export class Module {
|
||||
}
|
||||
|
||||
public addController(controller: Type<Controller>) {
|
||||
this._controllers.set(controller.name, {
|
||||
this._controllers.set(
|
||||
controller.name,
|
||||
new InstanceWrapper({
|
||||
name: controller.name,
|
||||
metatype: controller,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
});
|
||||
scope: this.getClassScope(controller),
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addRelatedModule(module: any) {
|
||||
this._relatedModules.add(module);
|
||||
this._imports.add(module);
|
||||
}
|
||||
|
||||
public replace(toReplace: string | symbol | Type<any>, options: any) {
|
||||
@@ -363,7 +451,7 @@ export class Module {
|
||||
});
|
||||
}
|
||||
|
||||
public createModuleRefMetatype(): any {
|
||||
public createModuleReferenceType(): any {
|
||||
const self = this;
|
||||
return class extends ModuleRef {
|
||||
constructor() {
|
||||
@@ -391,4 +479,9 @@ export class Module {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getClassScope(provider: ProviderMetatype): Scope {
|
||||
const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, provider);
|
||||
return metadata && metadata.scope;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { INTERCEPTORS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller, NestInterceptor } from '@nestjs/common/interfaces';
|
||||
import { ConfigurationProvider } from '@nestjs/common/interfaces/configuration-provider.interface';
|
||||
import {
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
|
||||
|
||||
export class InterceptorsContextCreator extends ContextCreator {
|
||||
private moduleContext: string;
|
||||
@@ -24,13 +22,20 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
instance: Controller,
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): NestInterceptor[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(instance, callback, INTERCEPTORS_METADATA);
|
||||
return this.createContext(
|
||||
instance,
|
||||
callback,
|
||||
INTERCEPTORS_METADATA,
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
@@ -40,7 +45,7 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
(interceptor: any) =>
|
||||
interceptor && (interceptor.name || interceptor.intercept),
|
||||
)
|
||||
.map(interceptor => this.getInterceptorInstance(interceptor))
|
||||
.map(interceptor => this.getInterceptorInstance(interceptor, contextId))
|
||||
.filter(
|
||||
(interceptor: NestInterceptor) =>
|
||||
interceptor && isFunction(interceptor.intercept),
|
||||
@@ -48,20 +53,25 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
.toArray() as R;
|
||||
}
|
||||
|
||||
public getInterceptorInstance(interceptor: Function | NestInterceptor) {
|
||||
public getInterceptorInstance(
|
||||
interceptor: Function | NestInterceptor,
|
||||
contextId: ContextId,
|
||||
) {
|
||||
const isObject = (interceptor as NestInterceptor).intercept;
|
||||
if (isObject) {
|
||||
return interceptor;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(interceptor);
|
||||
return instanceWrapper && instanceWrapper.instance
|
||||
? instanceWrapper.instance
|
||||
: null;
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype<T extends Record<string, any> = any>(
|
||||
metatype: T,
|
||||
): { instance: any } | undefined {
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
|
||||
import { NestMiddleware } from '@nestjs/common/interfaces/middleware/nest-middleware.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { InstanceWrapper } from './../injector/instance-wrapper';
|
||||
|
||||
export class MiddlewareContainer {
|
||||
private readonly middleware = new Map<
|
||||
string,
|
||||
Map<string, MiddlewareWrapper>
|
||||
>();
|
||||
private readonly middleware = new Map<string, Map<string, InstanceWrapper>>();
|
||||
private readonly configurationSets = new Map<
|
||||
string,
|
||||
Set<MiddlewareConfiguration>
|
||||
>();
|
||||
|
||||
public getMiddleware(module: string): Map<string, MiddlewareWrapper> {
|
||||
public getMiddleware(module: string): Map<string, InstanceWrapper> {
|
||||
return this.middleware.get(module) || new Map();
|
||||
}
|
||||
|
||||
@@ -22,24 +18,28 @@ export class MiddlewareContainer {
|
||||
|
||||
public addConfig(configList: MiddlewareConfiguration[], module: string) {
|
||||
const middleware = this.getCurrentMiddleware(module);
|
||||
const currentConfig = this.getCurrentConfig(module);
|
||||
const targetConfig = this.getCurrentConfig(module);
|
||||
|
||||
const configurations = configList || [];
|
||||
configurations.forEach(config => {
|
||||
[].concat(config.middleware).map(metatype => {
|
||||
const callback = metatype => {
|
||||
const token = metatype.name;
|
||||
middleware.set(token, {
|
||||
middleware.set(
|
||||
token,
|
||||
new InstanceWrapper({
|
||||
instance: null,
|
||||
metatype,
|
||||
});
|
||||
});
|
||||
currentConfig.add(config);
|
||||
}),
|
||||
);
|
||||
};
|
||||
[].concat(config.middleware).map(callback);
|
||||
targetConfig.add(config);
|
||||
});
|
||||
}
|
||||
|
||||
private getCurrentMiddleware(module: string) {
|
||||
if (!this.middleware.has(module)) {
|
||||
this.middleware.set(module, new Map<string, MiddlewareWrapper>());
|
||||
this.middleware.set(module, new Map<string, InstanceWrapper>());
|
||||
}
|
||||
return this.middleware.get(module);
|
||||
}
|
||||
@@ -51,8 +51,3 @@ export class MiddlewareContainer {
|
||||
return this.configurationSets.get(module);
|
||||
}
|
||||
}
|
||||
|
||||
export interface MiddlewareWrapper {
|
||||
instance: NestMiddleware;
|
||||
metatype: Type<NestMiddleware>;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,12 @@ import { InvalidMiddlewareException } from '../errors/exceptions/invalid-middlew
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { RouterExceptionFilters } from '../router/router-exception-filters';
|
||||
import { RouterProxy } from '../router/router-proxy';
|
||||
import { MiddlewareBuilder } from './builder';
|
||||
import { MiddlewareContainer, MiddlewareWrapper } from './container';
|
||||
import { MiddlewareContainer } from './container';
|
||||
import { MiddlewareResolver } from './resolver';
|
||||
import { RoutesMapper } from './routes-mapper';
|
||||
|
||||
@@ -140,7 +141,7 @@ export class MiddlewareModule {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
const { instance } = middleware as MiddlewareWrapper;
|
||||
const { instance } = middleware as InstanceWrapper;
|
||||
await this.bindHandler(
|
||||
instance,
|
||||
metatype,
|
||||
@@ -181,8 +182,7 @@ export class MiddlewareModule {
|
||||
middlewareInstance,
|
||||
path,
|
||||
);
|
||||
const resolve = instance.resolve();
|
||||
const middleware = await resolve;
|
||||
const middleware = await instance.resolve();
|
||||
bindWithProxy(middleware);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MiddlewareContainer, MiddlewareWrapper } from './container';
|
||||
import { Injector } from '../injector/injector';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { MiddlewareContainer } from './container';
|
||||
|
||||
export class MiddlewareResolver {
|
||||
private readonly instanceLoader = new Injector();
|
||||
@@ -10,21 +11,17 @@ export class MiddlewareResolver {
|
||||
public async resolveInstances(module: Module, moduleName: string) {
|
||||
const middleware = this.middlewareContainer.getMiddleware(moduleName);
|
||||
await Promise.all(
|
||||
[...middleware.values()].map(
|
||||
async wrapper => this.resolveMiddlewareInstance(wrapper, middleware, module),
|
||||
[...middleware.values()].map(async wrapper =>
|
||||
this.resolveMiddlewareInstance(wrapper, middleware, module),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async resolveMiddlewareInstance(
|
||||
wrapper: MiddlewareWrapper,
|
||||
middleware: Map<string, MiddlewareWrapper>,
|
||||
wrapper: InstanceWrapper,
|
||||
middleware: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
) {
|
||||
await this.instanceLoader.loadInstanceOfMiddleware(
|
||||
wrapper,
|
||||
middleware,
|
||||
module,
|
||||
);
|
||||
await this.instanceLoader.loadMiddleware(wrapper, middleware, module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,13 @@ import {
|
||||
PipeTransform,
|
||||
Transform,
|
||||
} from '@nestjs/common/interfaces';
|
||||
import {
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { STATIC_CONTEXT } from './../injector/constants';
|
||||
|
||||
export class PipesContextCreator extends ContextCreator {
|
||||
private moduleContext: string;
|
||||
@@ -28,39 +26,43 @@ export class PipesContextCreator extends ContextCreator {
|
||||
instance: Controller,
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): Transform<any>[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(instance, callback, PIPES_METADATA);
|
||||
return this.createContext(instance, callback, PIPES_METADATA, contextId);
|
||||
}
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
}
|
||||
return iterate(metadata)
|
||||
.filter((pipe: any) => pipe && (pipe.name || pipe.transform))
|
||||
.map(pipe => this.getPipeInstance(pipe))
|
||||
.map(pipe => this.getPipeInstance(pipe, contextId))
|
||||
.filter(pipe => pipe && pipe.transform && isFunction(pipe.transform))
|
||||
.map(pipe => pipe.transform.bind(pipe))
|
||||
.toArray() as R;
|
||||
}
|
||||
|
||||
public getPipeInstance(pipe: Function | PipeTransform) {
|
||||
public getPipeInstance(pipe: Function | PipeTransform, contextId: ContextId) {
|
||||
const isObject = (pipe as PipeTransform).transform;
|
||||
if (isObject) {
|
||||
return pipe;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(pipe as Function);
|
||||
return instanceWrapper && instanceWrapper.instance
|
||||
? instanceWrapper.instance
|
||||
: null;
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype<T extends { name: string } = any>(
|
||||
metatype: T,
|
||||
): { instance: any } | undefined {
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
|
||||
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||
import { ContextId } from '../../injector/instance-wrapper';
|
||||
|
||||
export interface ExceptionsFilter {
|
||||
create(
|
||||
instance: Controller,
|
||||
callback: Function,
|
||||
module: string,
|
||||
contextId?: ContextId,
|
||||
): ExceptionsHandler;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { BaseExceptionFilterContext } from '../exceptions/base-exception-filter-context';
|
||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { RouterProxyCallback } from './router-proxy';
|
||||
|
||||
@@ -21,6 +22,7 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
instance: Controller,
|
||||
callback: RouterProxyCallback,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
): ExceptionsHandler {
|
||||
this.moduleContext = module;
|
||||
|
||||
@@ -29,6 +31,7 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
instance,
|
||||
callback,
|
||||
EXCEPTION_FILTERS_METADATA,
|
||||
contextId,
|
||||
);
|
||||
if (isEmpty(filters)) {
|
||||
return exceptionHandler;
|
||||
|
||||
@@ -24,6 +24,7 @@ import { FORBIDDEN_MESSAGE } from '../guards/constants';
|
||||
import { GuardsConsumer } from '../guards/guards-consumer';
|
||||
import { GuardsContextCreator } from '../guards/guards-context-creator';
|
||||
import { ContextUtils } from '../helpers/context-utils';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
|
||||
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
|
||||
import { PipesConsumer } from '../pipes/pipes-consumer';
|
||||
@@ -69,6 +70,7 @@ export class RouterExecutionContext {
|
||||
methodName: string,
|
||||
module: string,
|
||||
requestMethod: RequestMethod,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const metadata =
|
||||
this.contextUtils.reflectCallbackMetadata(
|
||||
@@ -78,16 +80,27 @@ export class RouterExecutionContext {
|
||||
) || {};
|
||||
const keys = Object.keys(metadata);
|
||||
const argsLength = this.contextUtils.getArgumentsLength(keys, metadata);
|
||||
const pipes = this.pipesContextCreator.create(instance, callback, module);
|
||||
const pipes = this.pipesContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
const paramtypes = this.contextUtils.reflectCallbackParamtypes(
|
||||
instance,
|
||||
methodName,
|
||||
);
|
||||
const guards = this.guardsContextCreator.create(instance, callback, module);
|
||||
const guards = this.guardsContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
const interceptors = this.interceptorsContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
const httpCode = this.reflectHttpStatusCode(callback);
|
||||
const paramsMetadata = this.exchangeKeysForValues(keys, metadata, module);
|
||||
|
||||
@@ -11,7 +11,9 @@ import { GuardsConsumer } from '../guards/guards-consumer';
|
||||
import { GuardsContextCreator } from '../guards/guards-context-creator';
|
||||
import { ROUTE_MAPPED_MESSAGE } from '../helpers/messages';
|
||||
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { Injector } from '../injector/injector';
|
||||
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
|
||||
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
|
||||
import { MetadataScanner } from '../metadata-scanner';
|
||||
@@ -34,12 +36,15 @@ export class RouterExplorer {
|
||||
private readonly routerMethodFactory = new RouterMethodFactory();
|
||||
private readonly logger = new Logger(RouterExplorer.name, true);
|
||||
|
||||
// TEMP
|
||||
private readonly injector = new Injector();
|
||||
|
||||
constructor(
|
||||
private readonly metadataScanner: MetadataScanner,
|
||||
container: NestContainer,
|
||||
private readonly container: NestContainer,
|
||||
private readonly routerProxy?: RouterProxy,
|
||||
private readonly exceptionsFilter?: ExceptionsFilter,
|
||||
private readonly config?: ApplicationConfig,
|
||||
config?: ApplicationConfig,
|
||||
) {
|
||||
this.executionContextCreator = new RouterExecutionContext(
|
||||
new RouteParamsFactory(),
|
||||
@@ -155,6 +160,10 @@ export class RouterExplorer {
|
||||
.get(router, requestMethod)
|
||||
.bind(router);
|
||||
|
||||
const stripSlash = (str: string) =>
|
||||
str[str.length - 1] === '/' ? str.slice(0, str.length - 1) : str;
|
||||
const fullPath = stripSlash(basePath) + path;
|
||||
// TODO:
|
||||
const proxy = this.createCallbackProxy(
|
||||
instance,
|
||||
targetCallback,
|
||||
@@ -162,10 +171,32 @@ export class RouterExplorer {
|
||||
module,
|
||||
requestMethod,
|
||||
);
|
||||
const stripSlash = (str: string) =>
|
||||
str[str.length - 1] === '/' ? str.slice(0, str.length - 1) : str;
|
||||
const fullPath = stripSlash(basePath) + path;
|
||||
routerMethod(stripSlash(fullPath) || '/', proxy);
|
||||
/*routerMethod(
|
||||
stripSlash(fullPath) || '/',
|
||||
async <TRequest, TResponse>(
|
||||
req: TRequest,
|
||||
res: TResponse,
|
||||
next: Function,
|
||||
) => {
|
||||
const ctx = { id: 2 }; // asyncId
|
||||
const contextInstance = await this.injector.loadControllerPerContext(
|
||||
instance,
|
||||
this.container.getModules(),
|
||||
module,
|
||||
ctx,
|
||||
);
|
||||
const proxy = this.createCallbackProxy(
|
||||
contextInstance,
|
||||
contextInstance[methodName],
|
||||
methodName,
|
||||
module,
|
||||
requestMethod,
|
||||
ctx,
|
||||
);
|
||||
proxy(req, res, next);
|
||||
},
|
||||
);*/
|
||||
}
|
||||
|
||||
private createCallbackProxy(
|
||||
@@ -174,6 +205,7 @@ export class RouterExplorer {
|
||||
methodName: string,
|
||||
module: string,
|
||||
requestMethod: RequestMethod,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
const executionContext = this.executionContextCreator.create(
|
||||
instance,
|
||||
@@ -181,11 +213,13 @@ export class RouterExplorer {
|
||||
methodName,
|
||||
module,
|
||||
requestMethod,
|
||||
contextId,
|
||||
);
|
||||
const exceptionFilter = this.exceptionsFilter.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
);
|
||||
return this.routerProxy.createProxy(executionContext, exceptionFilter);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import { Controller } from '@nestjs/common/interfaces/controllers/controller.int
|
||||
import { Logger } from '@nestjs/common/services/logger.service';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { CONTROLLER_MAPPING_MESSAGE } from '../helpers/messages';
|
||||
import { InstanceWrapper, NestContainer } from '../injector/container';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { MetadataScanner } from '../metadata-scanner';
|
||||
import { Resolver } from './interfaces/resolver.interface';
|
||||
import { RouterExceptionFilters } from './router-exception-filters';
|
||||
|
||||
@@ -91,14 +91,14 @@ export class DependenciesScanner {
|
||||
const modules = this.container.getModules();
|
||||
|
||||
for (const [token, { metatype }] of modules) {
|
||||
await this.reflectRelatedModules(metatype, token, metatype.name);
|
||||
await this.reflectImports(metatype, token, metatype.name);
|
||||
this.reflectProviders(metatype, token);
|
||||
this.reflectControllers(metatype, token);
|
||||
this.reflectExports(metatype, token);
|
||||
}
|
||||
}
|
||||
|
||||
public async reflectRelatedModules(
|
||||
public async reflectImports(
|
||||
module: Type<any>,
|
||||
token: string,
|
||||
context: string,
|
||||
@@ -111,7 +111,7 @@ export class DependenciesScanner {
|
||||
),
|
||||
];
|
||||
for (const related of modules) {
|
||||
await this.insertRelatedModule(related, token, context);
|
||||
await this.insertImport(related, token, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,18 +234,14 @@ export class DependenciesScanner {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async insertRelatedModule(
|
||||
related: any,
|
||||
token: string,
|
||||
context: string,
|
||||
) {
|
||||
public async insertImport(related: any, token: string, context: string) {
|
||||
if (isUndefined(related)) {
|
||||
throw new CircularDependencyException(context);
|
||||
}
|
||||
if (related && related.forwardRef) {
|
||||
return this.container.addRelatedModule(related.forwardRef(), token);
|
||||
return this.container.addImport(related.forwardRef(), token);
|
||||
}
|
||||
await this.container.addRelatedModule(related, token);
|
||||
await this.container.addImport(related, token);
|
||||
}
|
||||
|
||||
public isCustomProvider(
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('NestContainer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('bindGlobalsToRelatedModules', () => {
|
||||
describe('bindGlobalsToImports', () => {
|
||||
it('should call "bindGlobalModuleToModule" for every global module', () => {
|
||||
const global1 = { test: 1 };
|
||||
const global2 = { test: 2 };
|
||||
@@ -105,7 +105,7 @@ describe('NestContainer', () => {
|
||||
container,
|
||||
'bindGlobalModuleToModule',
|
||||
);
|
||||
container.bindGlobalsToRelatedModules({
|
||||
container.bindGlobalsToImports({
|
||||
addRelatedModule: sinon.spy(),
|
||||
} as any);
|
||||
expect(bindGlobalModuleToModuleSpy.calledTwice).to.be.true;
|
||||
|
||||
@@ -145,12 +145,12 @@ describe('Injector', () => {
|
||||
metatype: Test,
|
||||
name: 'Test',
|
||||
};
|
||||
injector.loadPrototypeOfInstance(test, moduleDeps.providers);
|
||||
injector.loadPrototype(test, moduleDeps.providers);
|
||||
expect(moduleDeps.providers.get('Test')).to.deep.equal(expectedResult);
|
||||
});
|
||||
|
||||
it('should return null when collection is nil', () => {
|
||||
const result = injector.loadPrototypeOfInstance(test, null);
|
||||
const result = injector.loadPrototype(test, null);
|
||||
expect(result).to.be.null;
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ describe('Injector', () => {
|
||||
const collection = {
|
||||
get: () => ({ isResolved: true }),
|
||||
};
|
||||
const result = injector.loadPrototypeOfInstance(test, collection as any);
|
||||
const result = injector.loadPrototype(test, collection as any);
|
||||
expect(result).to.be.null;
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ describe('Injector', () => {
|
||||
const collection = {
|
||||
get: () => ({ inject: [] }),
|
||||
};
|
||||
const result = injector.loadPrototypeOfInstance(test, collection as any);
|
||||
const result = injector.loadPrototype(test, collection as any);
|
||||
expect(result).to.be.null;
|
||||
});
|
||||
});
|
||||
@@ -200,7 +200,7 @@ describe('Injector', () => {
|
||||
set: (...args) => {},
|
||||
};
|
||||
|
||||
injector.loadInstanceOfMiddleware(
|
||||
injector.loadMiddleware(
|
||||
{ metatype: { name: '' } } as any,
|
||||
collection as any,
|
||||
null,
|
||||
@@ -216,7 +216,7 @@ describe('Injector', () => {
|
||||
set: (...args) => {},
|
||||
};
|
||||
|
||||
injector.loadInstanceOfMiddleware(
|
||||
injector.loadMiddleware(
|
||||
{ metatype: { name: '' } } as any,
|
||||
collection as any,
|
||||
null,
|
||||
@@ -237,7 +237,7 @@ describe('Injector', () => {
|
||||
const module = { controllers: [] };
|
||||
const wrapper = { test: 'test' };
|
||||
|
||||
await injector.loadInstanceOfController(wrapper as any, module as any);
|
||||
await injector.loadController(wrapper as any, module as any);
|
||||
expect(loadInstance.calledWith(wrapper, module.controllers, module)).to.be
|
||||
.true;
|
||||
});
|
||||
@@ -255,14 +255,14 @@ describe('Injector', () => {
|
||||
const module = { injectables: [] };
|
||||
const wrapper = { test: 'test' };
|
||||
|
||||
await injector.loadInstanceOfInjectable(wrapper as any, module as any);
|
||||
await injector.loadInjectable(wrapper as any, module as any);
|
||||
expect(loadInstance.calledWith(wrapper, module.injectables, module)).to.be
|
||||
.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupComponent', () => {
|
||||
let lookupComponentInRelatedModules: sinon.SinonStub;
|
||||
let lookupComponentInImports: sinon.SinonStub;
|
||||
const metatype = { name: 'test', metatype: { name: 'test' } };
|
||||
const wrapper: any = {
|
||||
name: 'Test',
|
||||
@@ -271,8 +271,8 @@ describe('Injector', () => {
|
||||
isResolved: false,
|
||||
};
|
||||
beforeEach(() => {
|
||||
lookupComponentInRelatedModules = sinon.stub();
|
||||
(injector as any).lookupComponentInRelatedModules = lookupComponentInRelatedModules;
|
||||
lookupComponentInImports = sinon.stub();
|
||||
(injector as any).lookupComponentInImports = lookupComponentInImports;
|
||||
});
|
||||
|
||||
it('should return object from collection if exists', async () => {
|
||||
@@ -290,8 +290,8 @@ describe('Injector', () => {
|
||||
expect(result).to.be.equal(instance);
|
||||
});
|
||||
|
||||
it('should call "lookupComponentInRelatedModules" when object is not in collection', async () => {
|
||||
lookupComponentInRelatedModules.returns({});
|
||||
it('should call "lookupComponentInImports" when object is not in collection', async () => {
|
||||
lookupComponentInImports.returns({});
|
||||
const collection = {
|
||||
has: () => false,
|
||||
};
|
||||
@@ -301,11 +301,11 @@ describe('Injector', () => {
|
||||
{ name: metatype.name, index: 0, dependencies: [] },
|
||||
wrapper,
|
||||
);
|
||||
expect(lookupComponentInRelatedModules.called).to.be.true;
|
||||
expect(lookupComponentInImports.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should throw "UnknownDependenciesException" when instanceWrapper is null and "exports" collection does not contain token', () => {
|
||||
lookupComponentInRelatedModules.returns(null);
|
||||
lookupComponentInImports.returns(null);
|
||||
const collection = {
|
||||
has: () => false,
|
||||
};
|
||||
@@ -321,7 +321,7 @@ describe('Injector', () => {
|
||||
});
|
||||
|
||||
it('should not throw "UnknownDependenciesException" instanceWrapper is not null', () => {
|
||||
lookupComponentInRelatedModules.returns({});
|
||||
lookupComponentInImports.returns({});
|
||||
const collection = {
|
||||
has: () => false,
|
||||
};
|
||||
@@ -337,20 +337,20 @@ describe('Injector', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupComponentInRelatedModules', () => {
|
||||
let loadInstanceOfComponent: sinon.SinonSpy;
|
||||
describe('lookupComponentInImports', () => {
|
||||
let loadProvider: sinon.SinonSpy;
|
||||
const metatype = { name: 'test' };
|
||||
const module = {
|
||||
relatedModules: new Map(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
loadInstanceOfComponent = sinon.spy();
|
||||
(injector as any).loadInstanceOfComponent = loadInstanceOfComponent;
|
||||
loadProvider = sinon.spy();
|
||||
(injector as any).loadProvider = loadProvider;
|
||||
});
|
||||
|
||||
it('should return null when there is no related modules', async () => {
|
||||
const result = await injector.lookupComponentInRelatedModules(
|
||||
const result = await injector.lookupComponentInImports(
|
||||
module as any,
|
||||
null,
|
||||
);
|
||||
@@ -374,10 +374,7 @@ describe('Injector', () => {
|
||||
] as any),
|
||||
};
|
||||
expect(
|
||||
injector.lookupComponentInRelatedModules(
|
||||
module as any,
|
||||
metatype as any,
|
||||
),
|
||||
injector.lookupComponentInImports(module as any, metatype as any),
|
||||
).to.be.eventually.eq(null);
|
||||
|
||||
module = {
|
||||
@@ -396,14 +393,11 @@ describe('Injector', () => {
|
||||
] as any),
|
||||
};
|
||||
expect(
|
||||
injector.lookupComponentInRelatedModules(
|
||||
module as any,
|
||||
metatype as any,
|
||||
),
|
||||
injector.lookupComponentInImports(module as any, metatype as any),
|
||||
).to.eventually.be.eq(null);
|
||||
});
|
||||
|
||||
it('should call "loadInstanceOfComponent" when component is not resolved', async () => {
|
||||
it('should call "loadProvider" when component is not resolved', async () => {
|
||||
const module = {
|
||||
relatedModules: new Map([
|
||||
[
|
||||
@@ -423,14 +417,11 @@ describe('Injector', () => {
|
||||
],
|
||||
] as any),
|
||||
};
|
||||
await injector.lookupComponentInRelatedModules(
|
||||
module as any,
|
||||
metatype as any,
|
||||
);
|
||||
expect(loadInstanceOfComponent.called).to.be.true;
|
||||
await injector.lookupComponentInImports(module as any, metatype as any);
|
||||
expect(loadProvider.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should not call "loadInstanceOfComponent" when component is resolved', async () => {
|
||||
it('should not call "loadProvider" when component is resolved', async () => {
|
||||
const module = {
|
||||
relatedModules: new Map([
|
||||
[
|
||||
@@ -450,11 +441,8 @@ describe('Injector', () => {
|
||||
],
|
||||
] as any),
|
||||
};
|
||||
await injector.lookupComponentInRelatedModules(
|
||||
module as any,
|
||||
metatype as any,
|
||||
);
|
||||
expect(loadInstanceOfComponent.called).to.be.false;
|
||||
await injector.lookupComponentInImports(module as any, metatype as any);
|
||||
expect(loadProvider.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -506,9 +494,9 @@ describe('Injector', () => {
|
||||
});
|
||||
|
||||
describe('when instanceWrapper is not resolved and does not have forward ref', () => {
|
||||
it('should call loadInstanceOfComponent', async () => {
|
||||
it('should call loadProvider', async () => {
|
||||
const loadStub = sinon
|
||||
.stub(injector, 'loadInstanceOfComponent')
|
||||
.stub(injector, 'loadProvider')
|
||||
.callsFake(() => null);
|
||||
sinon.stub(injector, 'lookupComponent').returns({ isResolved: false });
|
||||
|
||||
@@ -520,9 +508,9 @@ describe('Injector', () => {
|
||||
);
|
||||
expect(loadStub.called).to.be.true;
|
||||
});
|
||||
it('should not call loadInstanceOfComponent (isResolved)', async () => {
|
||||
it('should not call loadProvider (isResolved)', async () => {
|
||||
const loadStub = sinon
|
||||
.stub(injector, 'loadInstanceOfComponent')
|
||||
.stub(injector, 'loadProvider')
|
||||
.callsFake(() => null);
|
||||
sinon.stub(injector, 'lookupComponent').returns({ isResolved: true });
|
||||
|
||||
@@ -534,9 +522,9 @@ describe('Injector', () => {
|
||||
);
|
||||
expect(loadStub.called).to.be.false;
|
||||
});
|
||||
it('should not call loadInstanceOfComponent (forwardRef)', async () => {
|
||||
it('should not call loadProvider (forwardRef)', async () => {
|
||||
const loadStub = sinon
|
||||
.stub(injector, 'loadInstanceOfComponent')
|
||||
.stub(injector, 'loadProvider')
|
||||
.callsFake(() => null);
|
||||
sinon
|
||||
.stub(injector, 'lookupComponent')
|
||||
@@ -555,7 +543,7 @@ describe('Injector', () => {
|
||||
describe('when instanceWraper has async property', () => {
|
||||
it('should await instance', async () => {
|
||||
const loadStub = sinon
|
||||
.stub(injector, 'loadInstanceOfComponent')
|
||||
.stub(injector, 'loadProvider')
|
||||
.callsFake(() => null);
|
||||
|
||||
const instance = Promise.resolve(true);
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('InstanceLoader', () => {
|
||||
mockContainer = sinon.mock(container);
|
||||
});
|
||||
|
||||
it('should call "loadPrototypeOfInstance" for each provider and route in each module', async () => {
|
||||
it('should call "loadPrototype" for each provider and route in each module', async () => {
|
||||
const injector = new Injector();
|
||||
(loader as any).injector = injector;
|
||||
|
||||
@@ -47,13 +47,10 @@ describe('InstanceLoader', () => {
|
||||
modules.set('Test', module);
|
||||
mockContainer.expects('getModules').returns(modules);
|
||||
|
||||
const loadProviderPrototypeStub = sinon.stub(
|
||||
injector,
|
||||
'loadPrototypeOfInstance',
|
||||
);
|
||||
const loadProviderPrototypeStub = sinon.stub(injector, 'loadPrototype');
|
||||
|
||||
sinon.stub(injector, 'loadInstanceOfController');
|
||||
sinon.stub(injector, 'loadInstanceOfComponent');
|
||||
sinon.stub(injector, 'loadController');
|
||||
sinon.stub(injector, 'loadProvider');
|
||||
|
||||
await loader.createInstancesOfDependencies();
|
||||
expect(
|
||||
@@ -64,7 +61,7 @@ describe('InstanceLoader', () => {
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('should call "loadInstanceOfComponent" for each provider in each module', async () => {
|
||||
it('should call "loadProvider" for each provider in each module', async () => {
|
||||
const injector = new Injector();
|
||||
(loader as any).injector = injector;
|
||||
|
||||
@@ -86,8 +83,8 @@ describe('InstanceLoader', () => {
|
||||
modules.set('Test', module);
|
||||
mockContainer.expects('getModules').returns(modules);
|
||||
|
||||
const loadProviderStub = sinon.stub(injector, 'loadInstanceOfComponent');
|
||||
sinon.stub(injector, 'loadInstanceOfController');
|
||||
const loadProviderStub = sinon.stub(injector, 'loadProvider');
|
||||
sinon.stub(injector, 'loadController');
|
||||
|
||||
await loader.createInstancesOfDependencies();
|
||||
expect(
|
||||
@@ -95,7 +92,7 @@ describe('InstanceLoader', () => {
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('should call "loadInstanceOfController" for each route in each module', async () => {
|
||||
it('should call "loadController" for each route in each module', async () => {
|
||||
const injector = new Injector();
|
||||
(loader as any).injector = injector;
|
||||
|
||||
@@ -112,8 +109,8 @@ describe('InstanceLoader', () => {
|
||||
modules.set('Test', module);
|
||||
mockContainer.expects('getModules').returns(modules);
|
||||
|
||||
sinon.stub(injector, 'loadInstanceOfProvider');
|
||||
const loadRoutesStub = sinon.stub(injector, 'loadInstanceOfController');
|
||||
sinon.stub(injector, 'loadProvider');
|
||||
const loadRoutesStub = sinon.stub(injector, 'loadController');
|
||||
|
||||
await loader.createInstancesOfDependencies();
|
||||
expect(
|
||||
@@ -121,7 +118,7 @@ describe('InstanceLoader', () => {
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('should call "loadInstanceOfInjectable" for each injectable in each module', async () => {
|
||||
it('should call "loadInjectable" for each injectable in each module', async () => {
|
||||
const injector = new Injector();
|
||||
(loader as any).injector = injector;
|
||||
|
||||
@@ -142,8 +139,8 @@ describe('InstanceLoader', () => {
|
||||
modules.set('Test', module);
|
||||
mockContainer.expects('getModules').returns(modules);
|
||||
|
||||
const loadInjectableStub = sinon.stub(injector, 'loadInstanceOfInjectable');
|
||||
sinon.stub(injector, 'loadInstanceOfController');
|
||||
const loadInjectableStub = sinon.stub(injector, 'loadInjectable');
|
||||
sinon.stub(injector, 'loadController');
|
||||
|
||||
await loader.createInstancesOfDependencies();
|
||||
expect(
|
||||
|
||||
@@ -128,6 +128,7 @@ describe('Module', () => {
|
||||
const type = { name: 'TypeTest' };
|
||||
const provider = { provide: type, useClass: type, name: 'test' };
|
||||
let setSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
const collection = new Map();
|
||||
setSpy = sinon.spy(collection, 'set');
|
||||
@@ -261,7 +262,7 @@ describe('Module', () => {
|
||||
describe('relatedModules', () => {
|
||||
it('should return relatedModules', () => {
|
||||
const test = ['test'];
|
||||
(module as any)._relatedModules = test;
|
||||
(module as any)._imports = test;
|
||||
expect(module.relatedModules).to.be.eql(test);
|
||||
});
|
||||
});
|
||||
@@ -290,13 +291,13 @@ describe('Module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('createModuleRefMetatype', () => {
|
||||
describe('createModuleReferenceType', () => {
|
||||
let moduleRef;
|
||||
|
||||
class SimpleClass {}
|
||||
|
||||
beforeEach(() => {
|
||||
const Class = module.createModuleRefMetatype();
|
||||
const Class = module.createModuleReferenceType();
|
||||
moduleRef = new Class();
|
||||
});
|
||||
|
||||
@@ -321,7 +322,7 @@ describe('Module', () => {
|
||||
describe('when unit exists in related modules collection', () => {
|
||||
it('should behave as identity', () => {
|
||||
const metatype = { name: token };
|
||||
(module as any)._relatedModules = new Set([
|
||||
(module as any)._imports = new Set([
|
||||
new Module(metatype as any, [], new NestContainer()),
|
||||
]);
|
||||
expect(module.validateExportedProvider(token)).to.be.eql(token);
|
||||
|
||||
@@ -28,10 +28,10 @@ describe('MiddlewareResolver', () => {
|
||||
});
|
||||
|
||||
it('should resolve middleware instances from container', () => {
|
||||
const loadInstanceOfMiddleware = sinon.stub(
|
||||
const loadMiddleware = sinon.stub(
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
resolver['instanceLoader'],
|
||||
'loadInstanceOfMiddleware',
|
||||
'loadMiddleware',
|
||||
);
|
||||
const middleware = new Map();
|
||||
const wrapper = {
|
||||
@@ -44,10 +44,9 @@ describe('MiddlewareResolver', () => {
|
||||
mockContainer.expects('getMiddleware').returns(middleware);
|
||||
resolver.resolveInstances(module, null);
|
||||
|
||||
expect(loadInstanceOfMiddleware.callCount).to.be.equal(middleware.size);
|
||||
expect(loadInstanceOfMiddleware.calledWith(wrapper, middleware, module)).to
|
||||
.be.true;
|
||||
expect(loadMiddleware.callCount).to.be.equal(middleware.size);
|
||||
expect(loadMiddleware.calledWith(wrapper, middleware, module)).to.be.true;
|
||||
|
||||
loadInstanceOfMiddleware.restore();
|
||||
loadMiddleware.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ export class ClientRedis extends ClientProxy {
|
||||
return this.getOptionsProp<RedisOptions>(this.options, 'retryDelay') || 0;
|
||||
}
|
||||
|
||||
public createResponseCallback(): Function {
|
||||
public createResponseCallback(): (channel: string, buffer: string) => void {
|
||||
return (channel: string, buffer: string) => {
|
||||
const { err, response, isDisposed, id } = JSON.parse(
|
||||
buffer,
|
||||
|
||||
@@ -3,10 +3,8 @@ import { ApplicationConfig } from '@nestjs/core/application-config';
|
||||
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
|
||||
import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer';
|
||||
import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator';
|
||||
import {
|
||||
InstanceWrapper,
|
||||
NestContainer,
|
||||
} from '@nestjs/core/injector/container';
|
||||
import { NestContainer } from '@nestjs/core/injector/container';
|
||||
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
||||
import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-consumer';
|
||||
import { InterceptorsContextCreator } from '@nestjs/core/interceptors/interceptors-context-creator';
|
||||
import { PipesConsumer } from '@nestjs/core/pipes/pipes-consumer';
|
||||
|
||||
@@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
|
||||
import { ApplicationConfig } from '@nestjs/core/application-config';
|
||||
import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer';
|
||||
import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator';
|
||||
import {
|
||||
InstanceWrapper,
|
||||
NestContainer,
|
||||
} from '@nestjs/core/injector/container';
|
||||
import { NestContainer } from '@nestjs/core/injector/container';
|
||||
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
||||
import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-consumer';
|
||||
import { InterceptorsContextCreator } from '@nestjs/core/interceptors/interceptors-context-creator';
|
||||
import { PipesConsumer } from '@nestjs/core/pipes/pipes-consumer';
|
||||
|
||||
@@ -5,24 +5,33 @@ import {
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Scope,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { AsyncContext } from '@nestjs/core/hooks/async-context';
|
||||
import { Roles } from '../common/decorators/roles.decorator';
|
||||
import { RolesGuard } from '../common/guards/roles.guard';
|
||||
import { LoggingInterceptor } from '../common/interceptors/logging.interceptor';
|
||||
import { TransformInterceptor } from '../common/interceptors/transform.interceptor';
|
||||
import { ParseIntPipe } from '../common/pipes/parse-int.pipe';
|
||||
import { CatsService } from './cats.service';
|
||||
import { CatsService, Rawr } from './cats.service';
|
||||
import { CreateCatDto } from './dto/create-cat.dto';
|
||||
import { Cat } from './interfaces/cat.interface';
|
||||
|
||||
@Catch()
|
||||
@Controller('cats')
|
||||
@Controller('cats', {
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
@UseGuards(RolesGuard)
|
||||
@UseInterceptors(LoggingInterceptor, TransformInterceptor)
|
||||
export class CatsController {
|
||||
constructor(private readonly catsService: CatsService) {}
|
||||
constructor(
|
||||
private readonly catsService: CatsService,
|
||||
private readonly asyncContext: AsyncContext,
|
||||
private readonly rawr: Rawr,
|
||||
) {
|
||||
console.log('Cats controller has been created (request)');
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Roles('admin')
|
||||
@@ -32,6 +41,12 @@ export class CatsController {
|
||||
|
||||
@Get()
|
||||
async findAll(): Promise<Cat[]> {
|
||||
const random = Math.random();
|
||||
console.log(random, this.rawr);
|
||||
console.log(
|
||||
((this.asyncContext as any).internalStorage as Map<any, any>).size,
|
||||
);
|
||||
this.asyncContext.set('xd', random);
|
||||
return this.catsService.findAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MiddlewareConsumer, Module, Scope } from '@nestjs/common';
|
||||
import { AsyncContext } from '@nestjs/core/hooks/async-context';
|
||||
import { CatsController } from './cats.controller';
|
||||
import { CatsService } from './cats.service';
|
||||
import { CatsService, Rawr } from './cats.service';
|
||||
|
||||
export class Boom {
|
||||
boom() {
|
||||
return 'bum';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
controllers: [CatsController],
|
||||
providers: [CatsService],
|
||||
providers: [
|
||||
CatsService,
|
||||
Rawr,
|
||||
{
|
||||
provide: Boom,
|
||||
useFactory: () => {
|
||||
console.log('Boom has been created (lazy)');
|
||||
return new Boom();
|
||||
},
|
||||
scope: Scope.LAZY,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CatsModule {}
|
||||
export class CatsModule {
|
||||
constructor(private readonly asyncContext: AsyncContext) {}
|
||||
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => this.asyncContext.run(next))
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { AsyncContext } from '@nestjs/core/hooks/async-context';
|
||||
import { Boom } from './cats.module';
|
||||
import { Cat } from './interfaces/cat.interface';
|
||||
|
||||
@Injectable()
|
||||
export class Rawr {
|
||||
constructor() {
|
||||
console.log('rawr created (transient)');
|
||||
}
|
||||
}
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class CatsService {
|
||||
private readonly cats: Cat[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly asyncContext: AsyncContext,
|
||||
@Inject('Boom') private readonly instance: Boom,
|
||||
private readonly rawr: Rawr,
|
||||
) {
|
||||
console.log('CatsService has been created');
|
||||
}
|
||||
|
||||
create(cat: Cat) {
|
||||
this.cats.push(cat);
|
||||
}
|
||||
|
||||
findAll(): Cat[] {
|
||||
console.log(this.rawr);
|
||||
this.instance.boom();
|
||||
|
||||
console.log(this.asyncContext.get('xd'));
|
||||
return this.cats;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ dynamic: true })
|
||||
export class RolesGuard implements CanActivate {
|
||||
constructor(private readonly reflector: Reflector) {}
|
||||
constructor(private readonly reflector: Reflector) {
|
||||
console.log('guard');
|
||||
}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const roles = this.reflector.get<string[]>('roles', context.getHandler());
|
||||
@@ -12,7 +14,8 @@ export class RolesGuard implements CanActivate {
|
||||
}
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
const hasRole = () => user.roles.some((role) => !!roles.find((item) => item === role));
|
||||
const hasRole = () =>
|
||||
user.roles.some(role => !!roles.find(item => item === role));
|
||||
return user && user.roles && hasRole();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user