refactor: general refactor reorganize, rename things

This commit is contained in:
Kamil Myśliwiec
2018-11-24 16:01:29 +01:00
parent 385071ba6d
commit 1cb3ed7f87
57 changed files with 533 additions and 490 deletions

View File

@@ -1,10 +1,10 @@
import { Optional } from '../decorators';
import { Injectable } from '../decorators/core';
import { ArgumentMetadata, BadRequestException } from '../index';
import { ValidatorOptions } from '../interfaces/external/validator-options.interface';
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
import { loadPackage } from '../utils/load-package.util';
import { isNil } from '../utils/shared.utils';
import { Injectable } from '../decorators/core/component.decorator';
export interface ValidationPipeOptions extends ValidatorOptions {
transform?: boolean;

View File

@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { Component, MiddlewareFunction, Interceptor, mixin, Injectable } from '../../index';
import { Component, Injectable, Interceptor, mixin } from '../../index';
describe('@Component', () => {
@Component()

View File

@@ -1,6 +1,6 @@
import { Constructor } from './merge-with-values.util';
import { Injectable } from '../decorators/core';
import { NestMiddleware } from '../interfaces/middleware/nest-middleware.interface';
import { Injectable } from '../decorators/core/component.decorator';
import { Constructor } from './merge-with-values.util';
export const BindResolveMiddlewareValues = <
T extends Constructor<NestMiddleware>

View File

@@ -12,7 +12,7 @@ import { Module } from '../injector/module';
* @param instance The instance which should get the name from
*/
const getInstanceName = (instance: any) =>
(instance && (instance as Type<any>).name);
instance && (instance as Type<any>).name;
/**
* Returns the name of the dependency
@@ -20,13 +20,15 @@ const getInstanceName = (instance: any) =>
* (= injection token). As fallback it returns '+'
* @param dependency The dependency whichs name should get displayed
*/
const getDependencyName = (dependency: InjectorDependency) => getInstanceName(dependency) || dependency || '+';
const getDependencyName = (dependency: InjectorDependency) =>
getInstanceName(dependency) || dependency || '+';
/**
* Returns the name of the module
* Tries to get the class name. As fallback it returns 'current'.
* @param module The module which should get displayed
*/
const getModuleName = (module: Module) => (module && getInstanceName(module.metatype)) || 'current';
const getModuleName = (module: Module) =>
(module && getInstanceName(module.metatype)) || 'current';
export const UNKNOWN_DEPENDENCIES_MESSAGE = (
type: string,
@@ -45,7 +47,9 @@ export const UNKNOWN_DEPENDENCIES_MESSAGE = (
message += ` (`;
message += dependenciesName.join(', ');
message += `). Please make sure that the argument at index [${index}] is available in the ${getModuleName(module)} context.`;
message += `). Please make sure that the argument at index [${index}] is available in the ${getModuleName(
module,
)} context.`;
return message;
};
@@ -56,7 +60,7 @@ export const INVALID_MODULE_MESSAGE = (text, scope: string) =>
`Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it. (Read more https://docs.nestjs.com/advanced/circular-dependency.) Scope [${scope}]`;
export const UNKNOWN_EXPORT_MESSAGE = (text, module: string) =>
`Nest cannot export a component/module that is not a part of the currently processed module (${module}). Please verify whether each exported unit is available in this particular context.`;
`Nest cannot export a provider/module that is not a part of the currently processed module (${module}). Please verify whether each exported unit is available in this particular context.`;
export const INVALID_CLASS_MESSAGE = (text, value: any) =>
`ModuleRef cannot instantiate class (${value} is not constructable).`;

View File

@@ -44,7 +44,7 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
);
}
public isExceptionObject(err): err is Error {
public isExceptionObject(err: any): err is Error {
return isObject(err) && !!(err as Error).message;
}
}

View File

@@ -26,7 +26,10 @@ export class ExceptionsHandler extends BaseExceptionFilter {
this.filters = filters;
}
public invokeCustomFilters(exception, response): boolean {
public invokeCustomFilters<T = any>(
exception: T,
ctx: ArgumentsHost,
): boolean {
if (isEmpty(this.filters)) return false;
const filter = this.filters.find(({ exceptionMetatypes }) => {
@@ -37,7 +40,7 @@ export class ExceptionsHandler extends BaseExceptionFilter {
);
return hasMetatype;
});
filter && filter.func(exception, response);
filter && filter.func(exception, ctx);
return !!filter;
}
}

View File

@@ -123,19 +123,19 @@ export class ExternalContextCreator {
return '';
}
for (const [key, module] of [...this.modulesContainer.entries()]) {
if (this.findComponentByClassName(module, className)) {
if (this.findProviderByClassName(module, className)) {
return key;
}
}
return '';
}
public findComponentByClassName(module: Module, className: string): boolean {
const { components } = module;
const hasComponent = [...components.keys()].some(
component => component === className,
public findProviderByClassName(module: Module, className: string): boolean {
const { providers } = module;
const hasProvider = [...providers.keys()].some(
provider => provider === className,
);
return hasComponent;
return hasProvider;
}
public exchangeKeysForValues<TMetadata = any>(
@@ -211,7 +211,7 @@ export class ExternalContextCreator {
);
}
public async transformToResult(resultOrDeffered) {
public async transformToResult(resultOrDeffered: any) {
if (resultOrDeffered && isFunction(resultOrDeffered.subscribe)) {
return resultOrDeffered.toPromise();
}

View File

@@ -1,7 +1,8 @@
import { HttpServer } from '@nestjs/common';
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
export class RouterMethodFactory {
public get(target, requestMethod: RequestMethod) {
public get(target: HttpServer, requestMethod: RequestMethod): Function {
switch (requestMethod) {
case RequestMethod.POST:
return target.post;

View File

@@ -24,8 +24,8 @@ export class ContainerScanner {
contextModule: Partial<Module>,
): TResult {
const dependencies = new Map([
...contextModule.components,
...contextModule.routes,
...contextModule.providers,
...contextModule.controllers,
...contextModule.injectables,
]);
const name = isFunction(metatypeOrToken)
@@ -44,8 +44,8 @@ export class ContainerScanner {
}
const modules = this.container.getModules();
const initialValue = {
components: [],
routes: [],
providers: [],
controllers: [],
injectables: [],
};
const merge = <T = any>(
@@ -55,8 +55,8 @@ export class ContainerScanner {
this.flatContainer = ([...modules.values()].reduce(
(current, next) => ({
components: merge(current.components, next.components),
routes: merge(current.routes, next.routes),
providers: merge(current.providers, next.providers),
controllers: merge(current.controllers, next.controllers),
injectables: merge(current.injectables, next.injectables),
}),
initialValue,

View File

@@ -27,7 +27,7 @@ export class NestContainer {
private applicationRef: any;
constructor(
private readonly _applicationConfig: ApplicationConfig = void 0,
private readonly _applicationConfig: ApplicationConfig = undefined,
) {}
get applicationConfig(): ApplicationConfig | undefined {
@@ -120,15 +120,15 @@ export class NestContainer {
module.addRelatedModule(related);
}
public addComponent(component: Type<any>, token: string): string {
if (!component) {
public addProvider(provider: Type<any>, token: string): string {
if (!provider) {
throw new CircularDependencyException();
}
if (!this.modules.has(token)) {
throw new UnknownModuleException();
}
const module = this.modules.get(token);
return module.addComponent(component);
return module.addProvider(provider);
}
public addInjectable(injectable: Type<any>, token: string) {
@@ -139,12 +139,12 @@ export class NestContainer {
module.addInjectable(injectable);
}
public addExportedComponent(exportedComponent: Type<any>, token: string) {
public addExportedProvider(provider: Type<any>, token: string) {
if (!this.modules.has(token)) {
throw new UnknownModuleException();
}
const module = this.modules.get(token);
module.addExportedComponent(exportedComponent);
module.addExportedProvider(provider);
}
public addController(controller: Type<any>, token: string) {
@@ -152,7 +152,7 @@ export class NestContainer {
throw new UnknownModuleException();
}
const module = this.modules.get(token);
module.addRoute(controller);
module.addController(controller);
}
public clear() {

View File

@@ -84,12 +84,12 @@ export class Injector {
);
}
public async loadInstanceOfRoute(
public async loadInstanceOfController(
wrapper: InstanceWrapper<Controller>,
module: Module,
) {
const routes = module.routes;
await this.loadInstance<Controller>(wrapper, routes, module);
const controllers = module.controllers;
await this.loadInstance<Controller>(wrapper, controllers, module);
}
public async loadInstanceOfInjectable(
@@ -121,8 +121,8 @@ export class Injector {
wrapper: InstanceWrapper<Injectable>,
module: Module,
) {
const components = module.components;
await this.loadInstance<Injectable>(wrapper, components, module);
const providers = module.providers;
await this.loadInstance<Injectable>(wrapper, providers, module);
}
public applyDoneHook<T>(wrapper: InstanceWrapper<T>): () => void {
@@ -227,7 +227,11 @@ export class Injector {
module: Module,
) {
if (isUndefined(param)) {
throw new UndefinedDependencyException(wrapper.name, dependencyContext, module);
throw new UndefinedDependencyException(
wrapper.name,
dependencyContext,
module,
);
}
const token = this.resolveParamToken(wrapper, param);
return this.resolveComponentInstance<T>(
@@ -255,9 +259,9 @@ export class Injector {
dependencyContext: InjectorDependencyContext,
wrapper: InstanceWrapper<T>,
) {
const components = module.components;
const providers = module.providers;
const instanceWrapper = await this.lookupComponent(
components,
providers,
module,
{ ...dependencyContext, name },
wrapper,
@@ -272,7 +276,7 @@ export class Injector {
}
public async lookupComponent<T = any>(
components: Map<string, any>,
providers: Map<string, any>,
module: Module,
dependencyContext: InjectorDependencyContext,
wrapper: InstanceWrapper<T>,
@@ -280,7 +284,7 @@ export class Injector {
const { name } = dependencyContext;
const scanInExports = () =>
this.lookupComponentInExports(dependencyContext, module, wrapper);
return components.has(name) ? components.get(name) : scanInExports();
return providers.has(name) ? providers.get(name) : scanInExports();
}
public async lookupComponentInExports<T = any>(
@@ -293,7 +297,11 @@ export class Injector {
dependencyContext.name,
);
if (isNil(instanceWrapper)) {
throw new UnknownDependenciesException(wrapper.name, dependencyContext, module);
throw new UnknownDependenciesException(
wrapper.name,
dependencyContext,
module,
);
}
return instanceWrapper;
}
@@ -312,8 +320,8 @@ export class Injector {
continue;
}
moduleRegistry.push(relatedModule.id);
const { components, exports } = relatedModule;
if (!exports.has(name) || !components.has(name)) {
const { providers, exports } = relatedModule;
if (!exports.has(name) || !providers.has(name)) {
const instanceRef = await this.lookupComponentInRelatedModules(
relatedModule,
name,
@@ -324,7 +332,7 @@ export class Injector {
}
continue;
}
componentRef = components.get(name);
componentRef = providers.get(name);
if (!componentRef.isResolved && !componentRef.forwardRef) {
await this.loadInstanceOfComponent(componentRef, relatedModule);
break;

View File

@@ -21,18 +21,18 @@ export class InstanceLoader {
private createPrototypes(modules: Map<string, Module>) {
modules.forEach(module => {
this.createPrototypesOfComponents(module);
this.createPrototypesOfProviders(module);
this.createPrototypesOfInjectables(module);
this.createPrototypesOfRoutes(module);
this.createPrototypesOfControllers(module);
});
}
private async createInstances(modules: Map<string, Module>) {
await Promise.all(
[...modules.values()].map(async module => {
await this.createInstancesOfComponents(module);
await this.createInstancesOfProviders(module);
await this.createInstancesOfInjectables(module);
await this.createInstancesOfRoutes(module);
await this.createInstancesOfControllers(module);
const { name } = module.metatype;
this.logger.log(MODULE_INIT_MESSAGE`${name}`);
@@ -40,33 +40,36 @@ export class InstanceLoader {
);
}
private createPrototypesOfComponents(module: Module) {
module.components.forEach(wrapper => {
private createPrototypesOfProviders(module: Module) {
module.providers.forEach(wrapper => {
this.injector.loadPrototypeOfInstance<Injectable>(
wrapper,
module.components,
module.providers,
);
});
}
private async createInstancesOfComponents(module: Module) {
private async createInstancesOfProviders(module: Module) {
await Promise.all(
[...module.components.values()].map(
async wrapper => this.injector.loadInstanceOfComponent(wrapper, module),
[...module.providers.values()].map(async wrapper =>
this.injector.loadInstanceOfComponent(wrapper, module),
),
);
}
private createPrototypesOfRoutes(module: Module) {
module.routes.forEach(wrapper => {
this.injector.loadPrototypeOfInstance<Controller>(wrapper, module.routes);
private createPrototypesOfControllers(module: Module) {
module.controllers.forEach(wrapper => {
this.injector.loadPrototypeOfInstance<Controller>(
wrapper,
module.controllers,
);
});
}
private async createInstancesOfRoutes(module: Module) {
private async createInstancesOfControllers(module: Module) {
await Promise.all(
[...module.routes.values()].map(
async wrapper => this.injector.loadInstanceOfRoute(wrapper, module),
[...module.controllers.values()].map(async wrapper =>
this.injector.loadInstanceOfController(wrapper, module),
),
);
}
@@ -82,8 +85,8 @@ export class InstanceLoader {
private async createInstancesOfInjectables(module: Module) {
await Promise.all(
[...module.injectables.values()].map(
async wrapper => this.injector.loadInstanceOfInjectable(wrapper, module),
[...module.injectables.values()].map(async wrapper =>
this.injector.loadInstanceOfInjectable(wrapper, module),
),
);
}

View File

@@ -24,18 +24,18 @@ import { ModuleRef } from './module-ref';
import { ModulesContainer } from './modules-container';
import { HTTP_SERVER_REF } from './tokens';
export interface CustomComponent {
export interface CustomProvider {
provide: any;
name: string;
}
export type OpaqueToken = string | symbol | object | Type<any>;
export type CustomClass = CustomComponent & { useClass: Type<any> };
export type CustomFactory = CustomComponent & {
export type CustomClass = CustomProvider & { useClass: Type<any> };
export type CustomFactory = CustomProvider & {
useFactory: (...args) => any;
inject?: OpaqueToken[];
};
export type CustomValue = CustomComponent & { useValue: any };
export type ComponentMetatype =
export type CustomValue = CustomProvider & { useValue: any };
export type ProviderMetatype =
| Type<Injectable>
| CustomFactory
| CustomValue
@@ -44,9 +44,12 @@ export type ComponentMetatype =
export class Module {
private readonly _id: string;
private readonly _relatedModules = new Set<Module>();
private readonly _components = new Map<any, InstanceWrapper<Injectable>>();
private readonly _providers = new Map<any, InstanceWrapper<Injectable>>();
private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>();
private readonly _routes = new Map<string, InstanceWrapper<Controller>>();
private readonly _controllers = new Map<
string,
InstanceWrapper<Controller>
>();
private readonly _exports = new Set<string>();
constructor(
@@ -54,7 +57,7 @@ export class Module {
private readonly _scope: Type<any>[],
private readonly container: NestContainer,
) {
this.addCoreInjectables(container);
this.addCoreProviders(container);
this._id = randomStringGenerator();
}
@@ -70,16 +73,30 @@ export class Module {
return this._relatedModules;
}
get providers(): Map<string, InstanceWrapper<Injectable>> {
return this._providers;
}
/**
* Left for backward-compatibility reasons
*/
get components(): Map<string, InstanceWrapper<Injectable>> {
return this._components;
return this._providers;
}
/**
* Left for backward-compatibility reasons
*/
get routes(): Map<string, InstanceWrapper<Controller>> {
return this._controllers;
}
get injectables(): Map<string, InstanceWrapper<Injectable>> {
return this._injectables;
}
get routes(): Map<string, InstanceWrapper<Controller>> {
return this._routes;
get controllers(): Map<string, InstanceWrapper<Controller>> {
return this._controllers;
}
get exports(): Set<string> {
@@ -87,10 +104,10 @@ export class Module {
}
get instance(): NestModule {
if (!this._components.has(this._metatype.name)) {
if (!this._providers.has(this._metatype.name)) {
throw new RuntimeException();
}
const module = this._components.get(this._metatype.name);
const module = this._providers.get(this._metatype.name);
return module.instance as NestModule;
}
@@ -98,8 +115,8 @@ export class Module {
return this._metatype;
}
public addCoreInjectables(container: NestContainer) {
this.addModuleAsComponent();
public addCoreProviders(container: NestContainer) {
this.addModuleAsProvider();
this.addModuleRef();
this.addReflector(container.getReflector());
this.addApplicationRef(container.getApplicationRef());
@@ -110,7 +127,7 @@ export class Module {
public addModuleRef() {
const moduleRef = this.createModuleRefMetatype();
this._components.set(ModuleRef.name, {
this._providers.set(ModuleRef.name, {
name: ModuleRef.name,
metatype: ModuleRef as any,
isResolved: true,
@@ -118,8 +135,8 @@ export class Module {
});
}
public addModuleAsComponent() {
this._components.set(this._metatype.name, {
public addModuleAsProvider() {
this._providers.set(this._metatype.name, {
name: this._metatype.name,
metatype: this._metatype,
isResolved: false,
@@ -128,7 +145,7 @@ export class Module {
}
public addReflector(reflector: Reflector) {
this._components.set(Reflector.name, {
this._providers.set(Reflector.name, {
name: Reflector.name,
metatype: Reflector,
isResolved: true,
@@ -137,7 +154,7 @@ export class Module {
}
public addApplicationRef(applicationRef: any) {
this._components.set(HTTP_SERVER_REF, {
this._providers.set(HTTP_SERVER_REF, {
name: HTTP_SERVER_REF,
metatype: {} as any,
isResolved: true,
@@ -148,7 +165,7 @@ export class Module {
public addExternalContextCreator(
externalContextCreator: ExternalContextCreator,
) {
this._components.set(ExternalContextCreator.name, {
this._providers.set(ExternalContextCreator.name, {
name: ExternalContextCreator.name,
metatype: ExternalContextCreator,
isResolved: true,
@@ -157,7 +174,7 @@ export class Module {
}
public addModulesContainer(modulesContainer: ModulesContainer) {
this._components.set(ModulesContainer.name, {
this._providers.set(ModulesContainer.name, {
name: ModulesContainer.name,
metatype: ModulesContainer,
isResolved: true,
@@ -166,7 +183,7 @@ export class Module {
}
public addApplicationRefHost(applicationRefHost: ApplicationReferenceHost) {
this._components.set(ApplicationReferenceHost.name, {
this._providers.set(ApplicationReferenceHost.name, {
name: ApplicationReferenceHost.name,
metatype: ApplicationReferenceHost,
isResolved: true,
@@ -174,7 +191,7 @@ export class Module {
});
}
public addInjectable(injectable: Type<Injectable>) {
public addInjectable<T = Injectable>(injectable: Type<T>) {
if (this.isCustomProvider(injectable)) {
return this.addCustomProvider(injectable, this._injectables);
}
@@ -186,63 +203,63 @@ export class Module {
});
}
public addComponent(component: ComponentMetatype): string {
if (this.isCustomProvider(component)) {
return this.addCustomProvider(component, this._components);
public addProvider(provider: ProviderMetatype): string {
if (this.isCustomProvider(provider)) {
return this.addCustomProvider(provider, this._providers);
}
this._components.set((component as Type<Injectable>).name, {
name: (component as Type<Injectable>).name,
metatype: component as Type<Injectable>,
this._providers.set((provider as Type<Injectable>).name, {
name: (provider as Type<Injectable>).name,
metatype: provider as Type<Injectable>,
instance: null,
isResolved: false,
});
return (component as Type<Injectable>).name;
return (provider as Type<Injectable>).name;
}
public isCustomProvider(
component: ComponentMetatype,
): component is CustomClass | CustomFactory | CustomValue {
return !isNil((component as CustomComponent).provide);
provider: ProviderMetatype,
): provider is CustomClass | CustomFactory | CustomValue {
return !isNil((provider as CustomProvider).provide);
}
public addCustomProvider(
component: CustomFactory | CustomValue | CustomClass,
provider: CustomFactory | CustomValue | CustomClass,
collection: Map<string, any>,
): string {
const { provide } = component;
const { provide } = provider;
const name = isFunction(provide) ? provide.name : provide;
const componentWithName = {
...component,
const providerNamePair = {
...provider,
name,
};
if (this.isCustomClass(componentWithName))
this.addCustomClass(componentWithName, collection);
else if (this.isCustomValue(componentWithName))
this.addCustomValue(componentWithName, collection);
else if (this.isCustomFactory(componentWithName))
this.addCustomFactory(componentWithName, collection);
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);
return name;
}
public isCustomClass(component): component is CustomClass {
return !isUndefined((component as CustomClass).useClass);
public isCustomClass(provider): provider is CustomClass {
return !isUndefined((provider as CustomClass).useClass);
}
public isCustomValue(component): component is CustomValue {
return !isUndefined((component as CustomValue).useValue);
public isCustomValue(provider): provider is CustomValue {
return !isUndefined((provider as CustomValue).useValue);
}
public isCustomFactory(component): component is CustomFactory {
return !isUndefined((component as CustomFactory).useFactory);
public isCustomFactory(provider): provider is CustomFactory {
return !isUndefined((provider as CustomFactory).useFactory);
}
public isDynamicModule(exported): exported is DynamicModule {
return exported && exported.module;
}
public addCustomClass(component: CustomClass, collection: Map<string, any>) {
const { name, useClass } = component;
public addCustomClass(provider: CustomClass, collection: Map<string, any>) {
const { name, useClass } = provider;
collection.set(name, {
name,
metatype: useClass,
@@ -251,8 +268,8 @@ export class Module {
});
}
public addCustomValue(component: CustomValue, collection: Map<string, any>) {
const { name, useValue: value } = component;
public addCustomValue(provider: CustomValue, collection: Map<string, any>) {
const { name, useValue: value } = provider;
collection.set(name, {
name,
metatype: null,
@@ -264,10 +281,10 @@ export class Module {
}
public addCustomFactory(
component: CustomFactory,
provider: CustomFactory,
collection: Map<string, any>,
) {
const { name, useFactory: factory, inject } = component;
const { name, useFactory: factory, inject } = provider;
collection.set(name, {
name,
metatype: factory as any,
@@ -278,27 +295,27 @@ export class Module {
});
}
public addExportedComponent(
exportedComponent: ComponentMetatype | string | DynamicModule,
public addExportedProvider(
provider: ProviderMetatype | string | DynamicModule,
) {
const addExportedUnit = (token: string) =>
this._exports.add(this.validateExportedProvider(token));
if (this.isCustomProvider(exportedComponent as any)) {
return this.addCustomExportedComponent(exportedComponent as any);
} else if (isString(exportedComponent)) {
return addExportedUnit(exportedComponent);
} else if (this.isDynamicModule(exportedComponent)) {
const { module } = exportedComponent;
if (this.isCustomProvider(provider as any)) {
return this.addCustomExportedProvider(provider as any);
} else if (isString(provider)) {
return addExportedUnit(provider);
} else if (this.isDynamicModule(provider)) {
const { module } = provider;
return addExportedUnit(module.name);
}
addExportedUnit(exportedComponent.name);
addExportedUnit(provider.name);
}
public addCustomExportedComponent(
exportedComponent: CustomFactory | CustomValue | CustomClass,
public addCustomExportedProvider(
provider: CustomFactory | CustomValue | CustomClass,
) {
const provide = exportedComponent.provide;
const provide = provider.provide;
if (isString(provide) || isSymbol(provide)) {
return this._exports.add(this.validateExportedProvider(provide));
}
@@ -306,7 +323,7 @@ export class Module {
}
public validateExportedProvider(token: string) {
if (this._components.has(token)) {
if (this._providers.has(token)) {
return token;
}
const importedArray = [...this._relatedModules.values()];
@@ -323,22 +340,22 @@ export class Module {
return token;
}
public addRoute(route: Type<Controller>) {
this._routes.set(route.name, {
name: route.name,
metatype: route,
public addController(controller: Type<Controller>) {
this._controllers.set(controller.name, {
name: controller.name,
metatype: controller,
instance: null,
isResolved: false,
});
}
public addRelatedModule(relatedModule) {
this._relatedModules.add(relatedModule);
public addRelatedModule(module: any) {
this._relatedModules.add(module);
}
public replace(toReplace, options) {
if (options.isComponent) {
return this.addComponent({ provide: toReplace, ...options });
public replace(toReplace: string | symbol | Type<any>, options: any) {
if (options.isProvider) {
return this.addProvider({ provide: toReplace, ...options });
}
this.addInjectable({
provide: toReplace,

View File

@@ -1,6 +1,9 @@
import { HttpServer } from '@nestjs/common';
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
import { MiddlewareConfiguration, RouteInfo } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
import {
MiddlewareConfiguration,
RouteInfo,
} from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
import { NestMiddleware } from '@nestjs/common/interfaces/middleware/nest-middleware.interface';
import { NestModule } from '@nestjs/common/interfaces/modules/nest-module.interface';
import { Type } from '@nestjs/common/interfaces/type.interface';
@@ -63,13 +66,15 @@ export class MiddlewareModule {
instance: NestModule,
module: string,
) {
if (!instance.configure) return;
if (!instance.configure) {
return;
}
const middlewareBuilder = new MiddlewareBuilder(this.routesMapper);
instance.configure(middlewareBuilder);
if (!(middlewareBuilder instanceof MiddlewareBuilder)) return;
if (!(middlewareBuilder instanceof MiddlewareBuilder)) {
return;
}
const config = middlewareBuilder.build();
middlewareContainer.addConfig(config, module);
}

View File

@@ -80,11 +80,11 @@ export class NestApplicationContext implements INestApplicationContext {
}
protected async callModuleInitHook(module: Module): Promise<any> {
const components = [...module.components];
// The Module (class) instance is the first element of the components array
const providers = [...module.providers];
// Module (class) instance is the first element of the providers array
// Lifecycle hook has to be called once all classes are properly initialized
const [_, { instance: moduleClassInstance }] = components.shift();
const instances = [...module.routes, ...components];
const [_, { instance: moduleClassInstance }] = providers.shift();
const instances = [...module.controllers, ...providers];
await Promise.all(
iterate(instances)
@@ -110,11 +110,11 @@ export class NestApplicationContext implements INestApplicationContext {
}
protected async callModuleDestroyHook(module: Module): Promise<any> {
const components = [...module.components];
// The Module (class) instance is the first element of the components array
const providers = [...module.providers];
// Module (class) instance is the first element of the providers array
// Lifecycle hook has to be called once all classes are properly destroyed
const [_, { instance: moduleClassInstance }] = components.shift();
const instances = [...module.routes, ...components];
const [_, { instance: moduleClassInstance }] = providers.shift();
const instances = [...module.controllers, ...providers];
await Promise.all(
iterate(instances)
@@ -143,9 +143,9 @@ export class NestApplicationContext implements INestApplicationContext {
}
protected async callModuleBootstrapHook(module: Module): Promise<any> {
const components = [...module.components];
const [_, { instance: moduleClassInstance }] = components.shift();
const instances = [...module.routes, ...components];
const providers = [...module.providers];
const [_, { instance: moduleClassInstance }] = providers.shift();
const instances = [...module.controllers, ...providers];
await Promise.all(
iterate(instances)

View File

@@ -38,12 +38,12 @@ export class RoutesResolver implements Resolver {
public resolve(appInstance, basePath: string) {
const modules = this.container.getModules();
modules.forEach(({ routes, metatype }, moduleName) => {
modules.forEach(({ controllers, metatype }, moduleName) => {
let path = metatype
? Reflect.getMetadata(MODULE_PATH, metatype)
: undefined;
path = path ? path + basePath : basePath;
this.registerRouters(routes, moduleName, path, appInstance);
this.registerRouters(controllers, moduleName, path, appInstance);
});
}
@@ -75,11 +75,7 @@ export class RoutesResolver implements Resolver {
const url = applicationRef.getRequestUrl(req);
throw new NotFoundException(`Cannot ${method} ${url}`);
};
const handler = this.routerExceptionsFilter.create(
{},
callback,
undefined,
);
const handler = this.routerExceptionsFilter.create({}, callback, undefined);
const proxy = this.routerProxy.createProxy(callback, handler);
applicationRef.setNotFoundHandler &&
applicationRef.setNotFoundHandler(proxy);

View File

@@ -1,13 +1,17 @@
import { DynamicModule, ForwardReference } from '@nestjs/common';
import { DynamicModule, ForwardReference, Provider } from '@nestjs/common';
import {
EXCEPTION_FILTERS_METADATA,
GATEWAY_MIDDLEWARES,
GUARDS_METADATA,
INTERCEPTORS_METADATA,
METADATA,
PIPES_METADATA,
ROUTE_ARGS_METADATA,
} from '@nestjs/common/constants';
import {
ClassProvider,
FactoryProvider,
ValueProvider,
} from '@nestjs/common/interfaces';
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
import { Type } from '@nestjs/common/interfaces/type.interface';
@@ -48,14 +52,14 @@ export class DependenciesScanner {
scope: Type<any>[] = [],
ctxRegistry: (ForwardReference | DynamicModule | Type<any>)[] = [],
) {
await this.storeModule(module, scope);
await this.insertModule(module, scope);
ctxRegistry.push(module);
if (this.isForwardReference(module)) {
module = (module as ForwardReference).forwardRef();
}
const modules = !this.isDynamicModule(module as Type<any> | DynamicModule)
? this.reflectMetadata(module, METADATA.MODULES)
? this.reflectMetadata(module as Type<any>, METADATA.MODULES)
: [
...this.reflectMetadata(
(module as DynamicModule).module,
@@ -76,7 +80,7 @@ export class DependenciesScanner {
}
}
public async storeModule(module: any, scope: Type<any>[]) {
public async insertModule(module: any, scope: Type<any>[]) {
if (module && module.forwardRef) {
return this.container.addModule(module.forwardRef(), scope);
}
@@ -88,7 +92,7 @@ export class DependenciesScanner {
for (const [token, { metatype }] of modules) {
await this.reflectRelatedModules(metatype, token, metatype.name);
this.reflectComponents(metatype, token);
this.reflectProviders(metatype, token);
this.reflectControllers(metatype, token);
this.reflectExports(metatype, token);
}
@@ -111,12 +115,12 @@ export class DependenciesScanner {
),
];
for (const related of modules) {
await this.storeRelatedModule(related, token, context);
await this.insertRelatedModule(related, token, context);
}
}
public reflectComponents(module: Type<any>, token: string) {
const components = [
public reflectProviders(module: Type<any>, token: string) {
const providers = [
...this.reflectMetadata(module, METADATA.COMPONENTS),
...this.container.getDynamicMetadataByToken(
token,
@@ -127,17 +131,12 @@ export class DependenciesScanner {
METADATA.PROVIDERS as 'providers',
),
];
components.forEach(component => {
this.storeComponent(component, token);
this.reflectComponentMetadata(component, token);
this.reflectDynamicMetadata(component, token);
providers.forEach(provider => {
this.insertProvider(provider, token);
this.reflectDynamicMetadata(provider, token);
});
}
public reflectComponentMetadata(component: Type<Injectable>, token: string) {
this.reflectGatewaysMiddleware(component, token);
}
public reflectControllers(module: Type<any>, token: string) {
const routes = [
...this.reflectMetadata(module, METADATA.CONTROLLERS),
@@ -147,7 +146,7 @@ export class DependenciesScanner {
),
];
routes.forEach(route => {
this.storeRoute(route, token);
this.insertController(route, token);
this.reflectDynamicMetadata(route, token);
});
}
@@ -171,16 +170,11 @@ export class DependenciesScanner {
METADATA.EXPORTS as 'exports',
),
];
exports.forEach(exportedComponent =>
this.storeExportedComponent(exportedComponent, token),
exports.forEach(exportedProvider =>
this.insertExportedProvider(exportedProvider, token),
);
}
public reflectGatewaysMiddleware(component: Type<Injectable>, token: string) {
const middleware = this.reflectMetadata(component, GATEWAY_MIDDLEWARES);
middleware.forEach(ware => this.storeComponent(ware, token));
}
public reflectInjectables(
component: Type<Injectable>,
token: string,
@@ -202,7 +196,7 @@ export class DependenciesScanner {
].filter(isFunction);
mergedInjectables.forEach(injectable =>
this.storeInjectable(injectable, token),
this.insertInjectable(injectable, token),
);
}
@@ -221,7 +215,7 @@ export class DependenciesScanner {
flatten(Object.keys(param).map(k => param[k].pipes)).filter(isFunction),
);
flatten(paramsInjectables).forEach(injectable =>
this.storeInjectable(injectable, token),
this.insertInjectable(injectable, token),
);
}
@@ -246,7 +240,7 @@ export class DependenciesScanner {
return undefined;
}
public async storeRelatedModule(
public async insertRelatedModule(
related: any,
token: string,
context: string,
@@ -260,17 +254,24 @@ export class DependenciesScanner {
await this.container.addRelatedModule(related, token);
}
public storeComponent(component, token: string) {
const isCustomProvider = component && !isNil(component.provide);
public isCustomProvider(
provider: Provider,
): provider is ClassProvider | ValueProvider | FactoryProvider {
return provider && !isNil((provider as any).provide);
}
public insertProvider(provider: Provider, token: string) {
const isCustomProvider = this.isCustomProvider(provider);
if (!isCustomProvider) {
return this.container.addComponent(component, token);
return this.container.addProvider(provider as Type<any>, token);
}
const applyProvidersMap = this.getApplyProvidersMap();
const providersKeys = Object.keys(applyProvidersMap);
const type = component.provide;
const type = (provider as ClassProvider | ValueProvider | FactoryProvider)
.provide;
if (!providersKeys.includes(type)) {
return this.container.addComponent(component, token);
return this.container.addProvider(provider as any, token);
}
const providerToken = randomStringGenerator();
this.applicationProvidersApplyMap.push({
@@ -278,31 +279,31 @@ export class DependenciesScanner {
moduleKey: token,
providerKey: providerToken,
});
this.container.addComponent(
this.container.addProvider(
{
...component,
...provider,
provide: providerToken,
},
} as any,
token,
);
}
public storeInjectable(component: Type<Injectable>, token: string) {
public insertInjectable(component: Type<Injectable>, token: string) {
this.container.addInjectable(component, token);
}
public storeExportedComponent(
exportedComponent: Type<Injectable>,
public insertExportedProvider(
exportedProvider: Type<Injectable>,
token: string,
) {
this.container.addExportedComponent(exportedComponent, token);
this.container.addExportedProvider(exportedProvider, token);
}
public storeRoute(route: Type<Controller>, token: string) {
this.container.addController(route, token);
public insertController(controller: Type<Controller>, token: string) {
this.container.addController(controller, token);
}
public reflectMetadata(metatype, metadataKey: string) {
public reflectMetadata(metatype: Type<any>, metadataKey: string) {
return Reflect.getMetadata(metadataKey, metatype) || [];
}
@@ -311,8 +312,8 @@ export class DependenciesScanner {
this.applicationProvidersApplyMap.forEach(
({ moduleKey, providerKey, type }) => {
const modules = this.container.getModules();
const { components } = modules.get(moduleKey);
const { instance } = components.get(providerKey);
const { providers } = modules.get(moduleKey);
const { instance } = providers.get(providerKey);
applyProvidersMap[type](instance);
},

View File

@@ -94,57 +94,57 @@ describe('ExternalContextCreator', () => {
expect(contextCreator.findContextModuleName({} as any)).to.be.eql('');
});
});
describe('when component exists', () => {
describe('when provider exists', () => {
it('should return module key', () => {
const modules = new Map();
const componentKey = 'test';
const providerKey = 'test';
const moduleKey = 'key';
modules.set(moduleKey, {});
(contextCreator as any).modulesContainer = modules;
sinon
.stub(contextCreator, 'findComponentByClassName')
.stub(contextCreator, 'findProviderByClassName')
.callsFake(() => true);
expect(
contextCreator.findContextModuleName({ name: componentKey } as any),
contextCreator.findContextModuleName({ name: providerKey } as any),
).to.be.eql(moduleKey);
});
});
describe('when component does not exists', () => {
describe('when provider does not exists', () => {
it('should return empty string', () => {
sinon
.stub(contextCreator, 'findComponentByClassName')
.stub(contextCreator, 'findProviderByClassName')
.callsFake(() => false);
expect(contextCreator.findContextModuleName({} as any)).to.be.eql('');
});
});
});
describe('findComponentByClassName', () => {
describe('when component exists', () => {
describe('findProviderByClassName', () => {
describe('when provider exists', () => {
it('should return true', () => {
const components = new Map();
const providers = new Map();
const key = 'test';
components.set(key, key);
providers.set(key, key);
expect(
contextCreator.findComponentByClassName(
contextCreator.findProviderByClassName(
{
components,
providers,
} as any,
key,
),
).to.be.true;
});
});
describe('when component does not exists', () => {
describe('when provider does not exists', () => {
it('should return false', () => {
const components = new Map();
const providers = new Map();
const key = 'test';
expect(
contextCreator.findComponentByClassName(
contextCreator.findProviderByClassName(
{
components,
providers,
} as any,
key,
),

View File

@@ -1,10 +1,11 @@
import { expect } from 'chai';
import { RouterMethodFactory } from '../../helpers/router-method-factory';
import { RequestMethod } from '../../../common/enums/request-method.enum';
import { RouterMethodFactory } from '../../helpers/router-method-factory';
describe('RouterMethodFactory', () => {
let factory: RouterMethodFactory;
const target = {
const target: any = {
get: () => {},
post: () => {},
use: () => {},

View File

@@ -20,14 +20,14 @@ describe('NestContainer', () => {
container = new NestContainer();
});
it('should "addComponent" throw "UnknownModuleException" when module is not stored in collection', () => {
expect(() => container.addComponent({} as any, 'TestModule')).throw(
it('should "addProvider" throw "UnknownModuleException" when module is not stored in collection', () => {
expect(() => container.addProvider({} as any, 'TestModule')).throw(
UnknownModuleException,
);
});
it('should "addComponent" throw "CircularDependencyException" when component is nil', () => {
expect(() => container.addComponent(null, 'TestModule')).throw(
it('should "addProvider" throw "CircularDependencyException" when provider is nil', () => {
expect(() => container.addProvider(null, 'TestModule')).throw(
CircularDependencyException,
);
});
@@ -38,8 +38,8 @@ describe('NestContainer', () => {
);
});
it('should "addExportedComponent" throw "UnknownModuleException" when module is not stored in collection', () => {
expect(() => container.addExportedComponent(null, 'TestModule')).throw(
it('should "addExportedProvider" throw "UnknownModuleException" when module is not stored in collection', () => {
expect(() => container.addExportedProvider(null, 'TestModule')).throw(
UnknownModuleException,
);
});

View File

@@ -2,7 +2,7 @@ import * as chai from 'chai';
import { expect } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import { Component } from '../../../common/decorators/core/component.decorator';
import { Injectable } from '../../../common/decorators/core/component.decorator';
import { Inject } from '../../../common/decorators/core/inject.decorator';
import { InstanceWrapper, NestContainer } from '../../injector/container';
import { Injector, PropertyDependency } from '../../injector/injector';
@@ -17,13 +17,13 @@ describe('Injector', () => {
});
describe('loadInstance', () => {
@Component()
@Injectable()
class DependencyOne {}
@Component()
@Injectable()
class DependencyTwo {}
@Component()
@Injectable()
class MainTest {
@Inject() property: DependencyOne;
@@ -53,18 +53,18 @@ describe('Injector', () => {
instance: Object.create(DependencyOne.prototype),
isResolved: false,
};
moduleDeps.components.set('MainTest', mainTest);
moduleDeps.components.set('DependencyOne', depOne);
moduleDeps.components.set('DependencyTwo', depTwo);
moduleDeps.components.set('MainTestResolved', {
moduleDeps.providers.set('MainTest', mainTest);
moduleDeps.providers.set('DependencyOne', depOne);
moduleDeps.providers.set('DependencyTwo', depTwo);
moduleDeps.providers.set('MainTestResolved', {
...mainTest,
isResolved: true,
});
});
it('should create an instance of component with proper dependencies', async () => {
await injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
const { instance } = moduleDeps.components.get(
await injector.loadInstance(mainTest, moduleDeps.providers, moduleDeps);
const { instance } = moduleDeps.providers.get(
'MainTest',
) as InstanceWrapper<MainTest>;
@@ -74,8 +74,8 @@ describe('Injector', () => {
});
it('should set "isResolved" property to true after instance initialization', async () => {
await injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
const { isResolved } = moduleDeps.components.get(
await injector.loadInstance(mainTest, moduleDeps.providers, moduleDeps);
const { isResolved } = moduleDeps.providers.get(
'MainTest',
) as InstanceWrapper<MainTest>;
expect(isResolved).to.be.true;
@@ -83,7 +83,7 @@ describe('Injector', () => {
it('should throw RuntimeException when type is not stored in collection', () => {
return expect(
injector.loadInstance({} as any, moduleDeps.components, moduleDeps),
injector.loadInstance({} as any, moduleDeps.providers, moduleDeps),
).to.eventually.be.rejected;
});
@@ -98,7 +98,7 @@ describe('Injector', () => {
isPending: true,
done$: Promise.resolve(value) as any,
},
moduleDeps.components,
moduleDeps.providers,
moduleDeps,
);
expect(result).to.be.eql(value);
@@ -113,7 +113,7 @@ describe('Injector', () => {
instance: Object.create(MainTest.prototype),
isResolved: true,
},
moduleDeps.components,
moduleDeps.providers,
moduleDeps,
);
expect(result).to.be.undefined;
@@ -121,7 +121,7 @@ describe('Injector', () => {
});
describe('loadPrototypeOfInstance', () => {
@Component()
@Injectable()
class Test {}
let moduleDeps: Module;
@@ -135,7 +135,7 @@ describe('Injector', () => {
instance: Object.create(Test.prototype),
isResolved: false,
};
moduleDeps.components.set('Test', test);
moduleDeps.providers.set('Test', test);
});
it('should create prototype of instance', () => {
@@ -145,8 +145,8 @@ describe('Injector', () => {
metatype: Test,
name: 'Test',
};
injector.loadPrototypeOfInstance(test, moduleDeps.components);
expect(moduleDeps.components.get('Test')).to.deep.equal(expectedResult);
injector.loadPrototypeOfInstance(test, moduleDeps.providers);
expect(moduleDeps.providers.get('Test')).to.deep.equal(expectedResult);
});
it('should return null when collection is nil', () => {
@@ -225,7 +225,7 @@ describe('Injector', () => {
});
});
describe('loadInstanceOfRoute', () => {
describe('loadInstanceOfController', () => {
let loadInstance: sinon.SinonSpy;
beforeEach(() => {
@@ -234,11 +234,11 @@ describe('Injector', () => {
});
it('should call "loadInstance" with expected arguments', async () => {
const module = { routes: [] };
const module = { controllers: [] };
const wrapper = { test: 'test' };
await injector.loadInstanceOfRoute(wrapper as any, module as any);
expect(loadInstance.calledWith(wrapper, module.routes, module)).to.be
await injector.loadInstanceOfController(wrapper as any, module as any);
expect(loadInstance.calledWith(wrapper, module.controllers, module)).to.be
.true;
});
});
@@ -363,7 +363,7 @@ describe('Injector', () => {
[
'key',
{
components: {
providers: {
has: () => false,
},
exports: {
@@ -385,7 +385,7 @@ describe('Injector', () => {
[
'key',
{
components: {
providers: {
has: () => true,
},
exports: {
@@ -409,7 +409,7 @@ describe('Injector', () => {
[
'key',
{
components: {
providers: {
has: () => true,
get: () => ({
isResolved: false,
@@ -436,7 +436,7 @@ describe('Injector', () => {
[
'key',
{
components: {
providers: {
has: () => true,
get: () => ({
isResolved: true,

View File

@@ -1,12 +1,12 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { InstanceLoader } from '../../injector/instance-loader';
import { NestContainer } from '../../injector/container';
import { Injector } from '../../injector/injector';
import * as sinon from 'sinon';
import { Injectable } from '../../../common';
import { Controller } from '../../../common/decorators/core/controller.decorator';
import { Component } from '../../../common/decorators/core/component.decorator';
import { NestEnvironment } from '../../../common/enums/nest-environment.enum';
import { Logger } from '../../../common/services/logger.service';
import { NestContainer } from '../../injector/container';
import { Injector } from '../../injector/injector';
import { InstanceLoader } from '../../injector/instance-loader';
describe('InstanceLoader', () => {
let loader: InstanceLoader;
@@ -16,8 +16,8 @@ describe('InstanceLoader', () => {
@Controller('')
class TestRoute {}
@Component()
class TestComponent {}
@Injectable()
class TestProvider {}
before(() => Logger.setMode(NestEnvironment.TEST));
@@ -27,103 +27,98 @@ describe('InstanceLoader', () => {
mockContainer = sinon.mock(container);
});
it('should call "loadPrototypeOfInstance" for each component and route in each module', async () => {
it('should call "loadPrototypeOfInstance" for each provider and route in each module', async () => {
const injector = new Injector();
(loader as any).injector = injector;
const module = {
components: new Map(),
routes: new Map(),
providers: new Map(),
controllers: new Map(),
injectables: new Map(),
metatype: { name: 'test' },
};
const componentWrapper = { instance: null, metatype: TestComponent };
const providerWrapper = { instance: null, metatype: TestProvider };
const routeWrapper = { instance: null, metatype: TestRoute };
module.components.set('TestComponent', componentWrapper);
module.routes.set('TestRoute', routeWrapper);
module.providers.set('TestProvider', providerWrapper);
module.controllers.set('TestRoute', routeWrapper);
const modules = new Map();
modules.set('Test', module);
mockContainer.expects('getModules').returns(modules);
const loadComponentPrototypeStub = sinon.stub(
const loadProviderPrototypeStub = sinon.stub(
injector,
'loadPrototypeOfInstance',
);
sinon.stub(injector, 'loadInstanceOfRoute');
sinon.stub(injector, 'loadInstanceOfController');
sinon.stub(injector, 'loadInstanceOfComponent');
await loader.createInstancesOfDependencies();
expect(
loadComponentPrototypeStub.calledWith(
componentWrapper,
module.components,
),
loadProviderPrototypeStub.calledWith(providerWrapper, module.providers),
).to.be.true;
expect(
loadComponentPrototypeStub.calledWith(routeWrapper, module.components),
loadProviderPrototypeStub.calledWith(routeWrapper, module.controllers),
).to.be.true;
});
it('should call "loadInstanceOfComponent" for each component in each module', async () => {
it('should call "loadInstanceOfComponent" for each provider in each module', async () => {
const injector = new Injector();
(loader as any).injector = injector;
const module = {
components: new Map(),
routes: new Map(),
providers: new Map(),
controllers: new Map(),
injectables: new Map(),
metatype: { name: 'test' },
};
const testComp = {
instance: null,
metatype: TestComponent,
name: 'TestComponent',
metatype: TestProvider,
name: 'TestProvider',
};
module.components.set('TestComponent', testComp);
module.providers.set('TestProvider', testComp);
const modules = new Map();
modules.set('Test', module);
mockContainer.expects('getModules').returns(modules);
const loadComponentStub = sinon.stub(injector, 'loadInstanceOfComponent');
sinon.stub(injector, 'loadInstanceOfRoute');
const loadProviderStub = sinon.stub(injector, 'loadInstanceOfComponent');
sinon.stub(injector, 'loadInstanceOfController');
await loader.createInstancesOfDependencies();
expect(
loadComponentStub.calledWith(
module.components.get('TestComponent'),
module,
),
loadProviderStub.calledWith(module.providers.get('TestProvider'), module),
).to.be.true;
});
it('should call "loadInstanceOfRoute" for each route in each module', async () => {
it('should call "loadInstanceOfController" for each route in each module', async () => {
const injector = new Injector();
(loader as any).injector = injector;
const module = {
components: new Map(),
routes: new Map(),
providers: new Map(),
controllers: new Map(),
injectables: new Map(),
metatype: { name: 'test' },
};
const wrapper = { name: 'TestRoute', instance: null, metatype: TestRoute };
module.routes.set('TestRoute', wrapper);
module.controllers.set('TestRoute', wrapper);
const modules = new Map();
modules.set('Test', module);
mockContainer.expects('getModules').returns(modules);
sinon.stub(injector, 'loadInstanceOfComponent');
const loadRoutesStub = sinon.stub(injector, 'loadInstanceOfRoute');
sinon.stub(injector, 'loadInstanceOfProvider');
const loadRoutesStub = sinon.stub(injector, 'loadInstanceOfController');
await loader.createInstancesOfDependencies();
expect(loadRoutesStub.calledWith(module.routes.get('TestRoute'), module)).to
.be.true;
expect(
loadRoutesStub.calledWith(module.controllers.get('TestRoute'), module),
).to.be.true;
});
it('should call "loadInstanceOfInjectable" for each injectable in each module', async () => {
@@ -131,29 +126,29 @@ describe('InstanceLoader', () => {
(loader as any).injector = injector;
const module = {
components: new Map(),
routes: new Map(),
providers: new Map(),
controllers: new Map(),
injectables: new Map(),
metatype: { name: 'test' },
};
const testComp = {
instance: null,
metatype: TestComponent,
name: 'TestComponent',
metatype: TestProvider,
name: 'TestProvider',
};
module.injectables.set('TestComponent', testComp);
module.injectables.set('TestProvider', testComp);
const modules = new Map();
modules.set('Test', module);
mockContainer.expects('getModules').returns(modules);
const loadInjectableStub = sinon.stub(injector, 'loadInstanceOfInjectable');
sinon.stub(injector, 'loadInstanceOfRoute');
sinon.stub(injector, 'loadInstanceOfController');
await loader.createInstancesOfDependencies();
expect(
loadInjectableStub.calledWith(
module.injectables.get('TestComponent'),
module.injectables.get('TestProvider'),
module,
),
).to.be.true;

View File

@@ -1,8 +1,8 @@
import { expect } from 'chai';
import safeStringify from 'fast-safe-stringify';
import * as hash from 'object-hash';
import { SingleScope } from '../../../common';
import { ModuleTokenFactory } from '../../injector/module-token-factory';
import safeStringify from 'fast-safe-stringify';
describe('ModuleTokenFactory', () => {
let factory: ModuleTokenFactory;
@@ -37,18 +37,14 @@ describe('ModuleTokenFactory', () => {
);
});
it('should include dynamic metadata', () => {
const token = factory.create(
SingleScope()(Module) as any,
[Module],
{
components: [{}],
} as any,
);
const token = factory.create(SingleScope()(Module) as any, [Module], {
providers: [{}],
} as any);
expect(token).to.be.deep.eq(
hash({
module: Module.name,
dynamic: safeStringify({
components: [{}],
providers: [{}],
}),
scope: [Module.name],
}),
@@ -64,7 +60,7 @@ describe('ModuleTokenFactory', () => {
describe('getDynamicMetadataToken', () => {
describe('when metadata exists', () => {
it('should return hash', () => {
const metadata = { components: ['', {}] };
const metadata = { providers: ['', {}] };
expect(factory.getDynamicMetadataToken(metadata as any)).to.be.eql(
JSON.stringify(metadata),
);

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai';
import * as sinon from 'sinon';
import { Component } from '../../../common/decorators/core/component.decorator';
import { Injectable } from '../../../common';
import { Module as ModuleDecorator } from '../../../common/decorators/modules/module.decorator';
import { RuntimeException } from '../../errors/exceptions/runtime.exception';
import { UnknownElementException } from '../../errors/exceptions/unknown-element.exception';
@@ -14,21 +14,22 @@ describe('Module', () => {
@ModuleDecorator({})
class TestModule {}
@Component()
class TestComponent {}
@Injectable()
class TestProvider {}
beforeEach(() => {
container = new NestContainer();
module = new Module(TestModule as any, [], container);
});
it('should add route', () => {
it('should add controller', () => {
const collection = new Map();
const setSpy = sinon.spy(collection, 'set');
(module as any)._routes = collection;
(module as any)._controllers = collection;
class Test {}
module.addRoute(Test);
module.addController(Test);
expect(setSpy.getCall(0).args).to.deep.equal([
'Test',
{
@@ -45,12 +46,12 @@ describe('Module', () => {
const setSpy = sinon.spy(collection, 'set');
(module as any)._injectables = collection;
module.addInjectable(TestComponent);
module.addInjectable(TestProvider);
expect(setSpy.getCall(0).args).to.deep.equal([
'TestComponent',
'TestProvider',
{
name: 'TestComponent',
metatype: TestComponent,
name: 'TestProvider',
metatype: TestProvider,
instance: null,
isResolved: false,
},
@@ -66,17 +67,17 @@ describe('Module', () => {
});
});
it('should add component', () => {
it('should add provider', () => {
const collection = new Map();
const setSpy = sinon.spy(collection, 'set');
(module as any)._components = collection;
(module as any)._providers = collection;
module.addComponent(TestComponent);
module.addProvider(TestProvider);
expect(setSpy.getCall(0).args).to.deep.equal([
'TestComponent',
'TestProvider',
{
name: 'TestComponent',
metatype: TestComponent,
name: 'TestProvider',
metatype: TestProvider,
instance: null,
isResolved: false,
},
@@ -89,7 +90,7 @@ describe('Module', () => {
const provider = { provide: 'test', useValue: 'test' };
module.addComponent(provider as any);
module.addProvider(provider as any);
expect((addCustomProvider as sinon.SinonSpy).called).to.be.true;
});
@@ -125,18 +126,18 @@ describe('Module', () => {
describe('addCustomClass', () => {
const type = { name: 'TypeTest' };
const component = { provide: type, useClass: type, name: 'test' };
const provider = { provide: type, useClass: type, name: 'test' };
let setSpy;
beforeEach(() => {
const collection = new Map();
setSpy = sinon.spy(collection, 'set');
(module as any)._components = collection;
(module as any)._providers = collection;
});
it('should store component', () => {
module.addCustomClass(component as any, (module as any)._components);
it('should store provider', () => {
module.addCustomClass(provider as any, (module as any)._providers);
expect(
setSpy.calledWith(component.name, {
name: component.name,
setSpy.calledWith(provider.name, {
name: provider.name,
metatype: type,
instance: null,
isResolved: false,
@@ -149,16 +150,16 @@ describe('Module', () => {
let setSpy;
const value = () => ({});
const name = 'test';
const component = { provide: value, name, useValue: value };
const provider = { provide: value, name, useValue: value };
beforeEach(() => {
const collection = new Map();
setSpy = sinon.spy(collection, 'set');
(module as any)._components = collection;
(module as any)._providers = collection;
});
it('should store component', () => {
module.addCustomValue(component as any, (module as any)._components);
it('should store provider', () => {
module.addCustomValue(provider as any, (module as any)._providers);
expect(
setSpy.calledWith(name, {
name,
@@ -175,20 +176,20 @@ describe('Module', () => {
describe('addCustomFactory', () => {
const type = { name: 'TypeTest' };
const inject = [1, 2, 3];
const component = { provide: type, useFactory: type, name: 'test', inject };
const provider = { provide: type, useFactory: type, name: 'test', inject };
let setSpy;
beforeEach(() => {
const collection = new Map();
setSpy = sinon.spy(collection, 'set');
(module as any)._components = collection;
(module as any)._providers = collection;
});
it('should store component', () => {
module.addCustomFactory(component as any, (module as any)._components);
it('should store provider', () => {
module.addCustomFactory(provider as any, (module as any)._providers);
expect(setSpy.getCall(0).args).to.deep.equal([
component.name,
provider.name,
{
name: component.name,
name: provider.name,
metatype: type,
instance: null,
isResolved: false,
@@ -200,52 +201,52 @@ describe('Module', () => {
});
describe('when get instance', () => {
describe('when metatype does not exists in components collection', () => {
describe('when metatype does not exists in providers collection', () => {
beforeEach(() => {
sinon.stub((module as any)._components, 'has').returns(false);
sinon.stub((module as any)._providers, 'has').returns(false);
});
it('should throws RuntimeException', () => {
expect(() => module.instance).to.throws(RuntimeException);
});
});
describe('when metatype exists in components collection', () => {
describe('when metatype exists in providers collection', () => {
it('should returns null', () => {
expect(module.instance).to.be.eql(null);
});
});
});
describe('when exported component is custom provided', () => {
describe('when exported provider is custom provided', () => {
beforeEach(() => {
sinon.stub(module, 'validateExportedProvider').callsFake(o => o);
});
it('should call `addCustomExportedComponent`', () => {
const addCustomExportedComponentSpy = sinon.spy(
it('should call `addCustomExportedProvider`', () => {
const addCustomExportedProviderSpy = sinon.spy(
module,
'addCustomExportedComponent',
'addCustomExportedProvider',
);
module.addExportedComponent({ provide: 'test' } as any);
expect(addCustomExportedComponentSpy.called).to.be.true;
module.addExportedProvider({ provide: 'test' } as any);
expect(addCustomExportedProviderSpy.called).to.be.true;
});
it('should support symbols', () => {
const addCustomExportedComponentSpy = sinon.spy(
const addCustomExportedProviderSpy = sinon.spy(
module,
'addCustomExportedComponent',
'addCustomExportedProvider',
);
const symb = Symbol('test');
module.addExportedComponent({ provide: symb } as any);
expect(addCustomExportedComponentSpy.called).to.be.true;
module.addExportedProvider({ provide: symb } as any);
expect(addCustomExportedProviderSpy.called).to.be.true;
expect((module as any)._exports.has(symb)).to.be.true;
});
});
describe('replace', () => {
describe('when component', () => {
it('should call `addComponent`', () => {
const addComponentSpy = sinon.spy(module, 'addComponent');
module.replace(null, { isComponent: true });
expect(addComponentSpy.called).to.be.true;
describe('when provider', () => {
it('should call `addProvider`', () => {
const addProviderSpy = sinon.spy(module, 'addProvider');
module.replace(null, { isProvider: true });
expect(addProviderSpy.called).to.be.true;
});
});
describe('when guard', () => {
@@ -273,11 +274,11 @@ describe('Module', () => {
});
});
describe('routes', () => {
it('should return routes', () => {
describe('controllers', () => {
it('should return controllers', () => {
const test = ['test'];
(module as any)._routes = test;
expect(module.routes).to.be.eql(test);
(module as any)._controllers = test;
expect(module.controllers).to.be.eql(test);
});
});
@@ -311,9 +312,9 @@ describe('Module', () => {
describe('validateExportedProvider', () => {
const token = 'token';
describe('when unit exists in component collection', () => {
describe('when unit exists in provider collection', () => {
it('should behave as identity', () => {
(module as any)._components = new Map([[token, true]]);
(module as any)._providers = new Map([[token, true]]);
expect(module.validateExportedProvider(token)).to.be.eql(token);
});
});
@@ -326,7 +327,7 @@ describe('Module', () => {
expect(module.validateExportedProvider(token)).to.be.eql(token);
});
});
describe('when unit does not exist in both component and related modules collections', () => {
describe('when unit does not exist in both provider and related modules collections', () => {
it('should throw UnknownExportException', () => {
expect(() => module.validateExportedProvider(token)).to.throws(
UnknownExportException,

View File

@@ -1,11 +1,11 @@
import { expect } from 'chai';
import { MiddlewareContainer } from '../../middleware/container';
import { MiddlewareConfiguration } from '../../../common/interfaces/middleware/middleware-configuration.interface';
import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface';
import { Component } from '../../../common/decorators/core/component.decorator';
import { Injectable } from '../../../common';
import { Controller } from '../../../common/decorators/core/controller.decorator';
import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator';
import { RequestMethod } from '../../../common/enums/request-method.enum';
import { MiddlewareConfiguration } from '../../../common/interfaces/middleware/middleware-configuration.interface';
import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface';
import { MiddlewareContainer } from '../../middleware/container';
describe('MiddlewareContainer', () => {
@Controller('test')
@@ -17,7 +17,7 @@ describe('MiddlewareContainer', () => {
public getAnother() {}
}
@Component()
@Injectable()
class TestMiddleware implements NestMiddleware {
public resolve() {
return (req, res, next) => {};

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { Component } from '../../../common/decorators/core/component.decorator';
import { Controller } from '../../../common/decorators/core/controller.decorator';
import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator';
import { RequestMethod } from '../../../common/enums/request-method.enum';
@@ -30,7 +30,7 @@ describe('MiddlewareModule', () => {
public getAnother() {}
}
@Component()
@Injectable()
class TestMiddleware implements NestMiddleware {
public resolve() {
return (req, res, next) => {};
@@ -93,7 +93,7 @@ describe('MiddlewareModule', () => {
});
it('should throw "InvalidMiddlewareException" exception when middleware does not have "resolve" method', () => {
@Component()
@Injectable()
class InvalidMiddleware {}
const route = { path: 'Test' };

View File

@@ -1,14 +1,14 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { MiddlewareResolver } from '../../middleware/resolver';
import { MiddlewareContainer } from '../../middleware/container';
import { Component } from '../../../common/decorators/core/component.decorator';
import * as sinon from 'sinon';
import { Injectable } from '../../../common';
import { NestEnvironment } from '../../../common/enums/nest-environment.enum';
import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface';
import { Logger } from '../../../common/services/logger.service';
import { NestEnvironment } from '../../../common/enums/nest-environment.enum';
import { MiddlewareContainer } from '../../middleware/container';
import { MiddlewareResolver } from '../../middleware/resolver';
describe('MiddlewareResolver', () => {
@Component()
@Injectable()
class TestMiddleware implements NestMiddleware {
public resolve() {
return (req, res, next) => {};
@@ -29,6 +29,7 @@ describe('MiddlewareResolver', () => {
it('should resolve middleware instances from container', () => {
const loadInstanceOfMiddleware = sinon.stub(
// tslint:disable-next-line:no-string-literal
resolver['instanceLoader'],
'loadInstanceOfMiddleware',
);
@@ -39,7 +40,7 @@ describe('MiddlewareResolver', () => {
};
middleware.set('TestMiddleware', wrapper);
const module = <any>{ metatype: { name: '' } };
const module = { metatype: { name: '' } } as any;
mockContainer.expects('getMiddleware').returns(middleware);
resolver.resolveInstances(module, null);

View File

@@ -1,27 +1,28 @@
import { Injectable } from '@nestjs/common';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { GUARDS_METADATA } from '../../common/constants';
import { Component } from '../../common/decorators/core/component.decorator';
import { Controller } from '../../common/decorators/core/controller.decorator';
import { UseGuards } from '../../common/decorators/core/use-guards.decorator';
import { Module } from '../../common/decorators/modules/module.decorator';
import { ApplicationConfig } from '../application-config';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '../constants';
import { MetadataScanner } from '../metadata-scanner';
import { NestContainer } from '../injector/container';
import { MetadataScanner } from '../metadata-scanner';
import { DependenciesScanner } from '../scanner';
class Guard {}
describe('DependenciesScanner', () => {
@Component()
@Injectable()
class TestComponent {}
@Controller('')
class TestRoute {}
class TestController {}
@Module({
providers: [TestComponent],
controllers: [TestRoute],
controllers: [TestController],
exports: [TestComponent],
})
class AnotherTestModule {}
@@ -29,7 +30,7 @@ describe('DependenciesScanner', () => {
@Module({
imports: [AnotherTestModule],
providers: [TestComponent],
controllers: [TestRoute],
controllers: [TestController],
})
class TestModule {}
@@ -52,29 +53,29 @@ describe('DependenciesScanner', () => {
mockContainer.restore();
});
it('should "storeModule" call twice (2 modules) container method "addModule"', async () => {
it('should "insertModule" call twice (2 modules) container method "addModule"', async () => {
const expectation = mockContainer.expects('addModule').twice();
await scanner.scan(TestModule as any);
expectation.verify();
});
it('should "storeComponent" call twice (2 components) container method "addComponent"', async () => {
const expectation = mockContainer.expects('addComponent').twice();
const stub = sinon.stub(scanner, 'storeExportedComponent');
it('should "insertProvider" call twice (2 components) container method "addProvider"', async () => {
const expectation = mockContainer.expects('addProvider').twice();
const stub = sinon.stub(scanner, 'insertExportedProvider');
await scanner.scan(TestModule as any);
expectation.verify();
stub.restore();
});
it('should "storeRoute" call twice (2 components) container method "addController"', async () => {
it('should "insertController" call twice (2 components) container method "addController"', async () => {
const expectation = mockContainer.expects('addController').twice();
await scanner.scan(TestModule as any);
expectation.verify();
});
it('should "storeExportedComponent" call once (1 component) container method "addExportedComponent"', async () => {
const expectation = mockContainer.expects('addExportedComponent').once();
it('should "insertExportedProvider" call once (1 component) container method "addExportedProvider"', async () => {
const expectation = mockContainer.expects('addExportedProvider').once();
await scanner.scan(TestModule as any);
expectation.verify();
});
@@ -112,7 +113,7 @@ describe('DependenciesScanner', () => {
});
});
describe('storeInjectable', () => {
describe('insertInjectable', () => {
it('should call "addInjectable"', () => {
const addInjectable = sinon
.stub((scanner as any).container, 'addInjectable')
@@ -120,7 +121,7 @@ describe('DependenciesScanner', () => {
const comp = {};
const token = 'token';
scanner.storeInjectable(comp as any, token);
scanner.insertInjectable(comp as any, token);
expect(addInjectable.calledWith(comp, token)).to.be.true;
});
});
@@ -144,67 +145,67 @@ describe('DependenciesScanner', () => {
});
});
describe('storeModule', () => {
describe('insertModule', () => {
it('should call forwardRef() when forwardRef property exists', () => {
const module = { forwardRef: sinon.spy() };
sinon.stub(container, 'addModule').returns({});
scanner.storeModule(module as any, [] as any);
scanner.insertModule(module as any, [] as any);
expect(module.forwardRef.called).to.be.true;
});
});
describe('storeRelatedModule', () => {
describe('insertRelatedModule', () => {
it('should call forwardRef() when forwardRef property exists', async () => {
const module = { forwardRef: sinon.stub().returns({}) };
sinon.stub(container, 'addRelatedModule').returns({});
await scanner.storeRelatedModule(module as any, [] as any, 'test');
await scanner.insertRelatedModule(module as any, [] as any, 'test');
expect(module.forwardRef.called).to.be.true;
});
describe('when "related" is nil', () => {
it('should throw exception', () => {
expect(
scanner.storeRelatedModule(undefined, [] as any, 'test'),
scanner.insertRelatedModule(undefined, [] as any, 'test'),
).to.eventually.throws();
});
});
});
describe('storeComponent', () => {
describe('insertProvider', () => {
const token = 'token';
describe('when component is not custom', () => {
it('should call container "addComponent" with expected args', () => {
const component = {};
describe('when provider is not custom', () => {
it('should call container "addProvider" with expected args', () => {
const provider = {};
const expectation = mockContainer
.expects('addComponent')
.withArgs(component, token);
.expects('addProvider')
.withArgs(provider, token);
mockContainer.expects('addComponent').callsFake(() => false);
scanner.storeComponent(component, token);
mockContainer.expects('addProvider').callsFake(() => false);
scanner.insertProvider(provider as any, token);
expectation.verify();
});
});
describe('when component is custom', () => {
describe('when provider is custom', () => {
describe('and is global', () => {
const component = {
const provider = {
provide: APP_INTERCEPTOR,
useValue: true,
};
it('should call container "addComponent" with expected args', () => {
const expectation = mockContainer.expects('addComponent').atLeast(1);
it('should call container "addProvider" with expected args', () => {
const expectation = mockContainer.expects('addProvider').atLeast(1);
mockContainer.expects('addComponent').callsFake(() => false);
scanner.storeComponent(component, token);
mockContainer.expects('addProvider').callsFake(() => false);
scanner.insertProvider(provider, token);
expectation.verify();
});
it('should push new object to "applicationProvidersApplyMap" array', () => {
mockContainer.expects('addComponent').callsFake(() => false);
scanner.storeComponent(component, token);
mockContainer.expects('addProvider').callsFake(() => false);
scanner.insertProvider(provider, token);
const applyMap = (scanner as any).applicationProvidersApplyMap;
expect(applyMap).to.have.length(1);
@@ -216,13 +217,13 @@ describe('DependenciesScanner', () => {
provide: 'CUSTOM',
useValue: true,
};
it('should call container "addComponent" with expected args', () => {
it('should call container "addProvider" with expected args', () => {
const expectation = mockContainer
.expects('addComponent')
.expects('addProvider')
.withArgs(component, token);
mockContainer.expects('addComponent').callsFake(() => false);
scanner.storeComponent(component, token);
mockContainer.expects('addProvider').callsFake(() => false);
scanner.insertProvider(component, token);
expectation.verify();
});
@@ -231,8 +232,8 @@ describe('DependenciesScanner', () => {
0,
);
mockContainer.expects('addComponent').callsFake(() => false);
scanner.storeComponent(component, token);
mockContainer.expects('addProvider').callsFake(() => false);
scanner.insertProvider(component, token);
expect((scanner as any).applicationProvidersApplyMap).to.have.length(
0,
);
@@ -252,7 +253,7 @@ describe('DependenciesScanner', () => {
const expectedInstance = {};
mockContainer.expects('getModules').callsFake(() => ({
get: () => ({
components: { get: () => ({ instance: expectedInstance }) },
providers: { get: () => ({ instance: expectedInstance }) },
}),
}));
const applySpy = sinon.spy();

View File

@@ -3,9 +3,9 @@ import { loadPackage } from '@nestjs/common/utils/load-package.util';
import { isObject } from '@nestjs/common/utils/shared.utils';
import { Observable } from 'rxjs';
import { GRPC_DEFAULT_URL } from '../constants';
import { InvalidGrpcPackageException } from '../exceptions/errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../exceptions/errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../exceptions/errors/invalid-proto-definition.exception';
import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception';
import { ClientGrpc, GrpcOptions } from '../interfaces';
import { ClientOptions } from '../interfaces/client-metadata.interface';
import { ClientProxy } from './client-proxy';

View File

@@ -10,7 +10,7 @@ import {
} from 'rxjs';
import { map, mergeMap, take } from 'rxjs/operators';
import { CONNECT_EVENT, ERROR_EVENT } from '../constants';
import { InvalidMessageException } from '../exceptions/errors/invalid-message.exception';
import { InvalidMessageException } from '../errors/invalid-message.exception';
import {
ClientOptions,
PacketId,

View File

@@ -27,8 +27,8 @@ export class RpcExceptionsHandler extends BaseRpcExceptionFilter {
this.filters = filters;
}
public invokeCustomFilters(
exception,
public invokeCustomFilters<T = any>(
exception: T,
host: ArgumentsHost,
): Observable<any> | null {
if (isEmpty(this.filters)) return null;

View File

@@ -1,8 +1,8 @@
import { fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CANCEL_EVENT, GRPC_DEFAULT_URL } from '../constants';
import { InvalidGrpcPackageException } from '../exceptions/errors/invalid-grpc-package.exception';
import { InvalidProtoDefinitionException } from '../exceptions/errors/invalid-proto-definition.exception';
import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception';
import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception';
import { CustomTransportStrategy } from '../interfaces';
import {
GrpcOptions,

View File

@@ -3,9 +3,9 @@ import { join } from 'path';
import { Observable } from 'rxjs';
import * as sinon from 'sinon';
import { ClientGrpcProxy } from '../../client/client-grpc';
import { InvalidGrpcPackageException } from '../../exceptions/errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../../exceptions/errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../../exceptions/errors/invalid-proto-definition.exception';
import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
// tslint:disable:no-string-literal
class GrpcService {

View File

@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { of } from 'rxjs';
import * as sinon from 'sinon';
import { Guard, Injectable, UseGuards, UsePipes } from '../../../common';
import { ApplicationConfig } from '../../../core/application-config';
import { GuardsConsumer } from '../../../core/guards/guards-consumer';
import { GuardsContextCreator } from '../../../core/guards/guards-context-creator';
@@ -9,17 +10,10 @@ import { InterceptorsConsumer } from '../../../core/interceptors/interceptors-co
import { InterceptorsContextCreator } from '../../../core/interceptors/interceptors-context-creator';
import { PipesConsumer } from '../../../core/pipes/pipes-consumer';
import { PipesContextCreator } from '../../../core/pipes/pipes-context-creator';
import { RpcException } from '../../index';
import {
Component,
Guard,
Injectable,
UseGuards,
UsePipes,
} from '../../../common';
import { ExceptionFiltersContext } from '../../context/exception-filters-context';
import { RpcContextCreator } from '../../context/rpc-context-creator';
import { RpcProxy } from '../../context/rpc-proxy';
import { RpcException } from '../../index';
@Guard()
class TestGuard {
@@ -45,7 +39,7 @@ describe('RpcContextCreator', () => {
let module: string;
@UseGuards(TestGuard)
@Component()
@Injectable()
class Test {
@UsePipes(new TestPipe())
test(data: string) {

View File

@@ -2,7 +2,7 @@ import { expect } from 'chai';
import { join } from 'path';
import { of } from 'rxjs';
import * as sinon from 'sinon';
import { InvalidGrpcPackageException } from '../../exceptions/errors/invalid-grpc-package.exception';
import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
import { ServerGrpc } from '../../server/server-grpc';
describe('ServerGrpc', () => {

View File

@@ -67,11 +67,11 @@ export class TestingModuleBuilder {
return new TestingModule(this.container, [], root, this.applicationConfig);
}
private override(typeOrToken, isComponent: boolean): OverrideBy {
private override(typeOrToken, isProvider: boolean): OverrideBy {
const addOverload = options => {
this.overloadsMap.set(typeOrToken, {
...options,
isComponent,
isProvider,
});
return this;
};

View File

@@ -7,8 +7,10 @@ import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';
import * as io from 'socket.io';
import { CONNECTION_EVENT, DISCONNECT_EVENT } from '../constants';
import { MessageMappingProperties } from '../gateway-metadata-explorer';
import { BaseWsExceptionFilter } from './../exceptions/base-ws-exception-filter';
export class IoAdapter implements WebSocketAdapter {
protected readonly baseExceptionFilter = new BaseWsExceptionFilter();
protected readonly httpServer: Server;
constructor(appOrHttpServer?: INestApplicationContext | Server) {
@@ -70,12 +72,14 @@ export class IoAdapter implements WebSocketAdapter {
}),
takeUntil(disconnect$),
);
source$.subscribe(([response, ack]) => {
const onMessage = ([response, ack]) => {
if (response.event) {
return client.emit(response.event, response.data);
}
isFunction(ack) && ack(response);
});
};
const onError = err => this.baseExceptionFilter.handleError(client, err);
source$.subscribe(onMessage as any, onError);
});
}

View File

@@ -11,6 +11,7 @@ import { EMPTY as empty, fromEvent, Observable } from 'rxjs';
import { filter, first, mergeMap, share, takeUntil } from 'rxjs/operators';
import { CLOSE_EVENT, CONNECTION_EVENT, ERROR_EVENT } from '../constants';
import { MessageMappingProperties } from '../gateway-metadata-explorer';
import { BaseWsExceptionFilter } from './../exceptions/base-ws-exception-filter';
let wsPackage: any = {};
@@ -22,6 +23,7 @@ enum READY_STATE {
}
export class WsAdapter implements WebSocketAdapter {
protected readonly baseExceptionFilter = new BaseWsExceptionFilter();
protected readonly logger = new Logger(WsAdapter.name);
protected readonly httpServer: Server;
@@ -79,13 +81,14 @@ export class WsAdapter implements WebSocketAdapter {
),
takeUntil(close$),
);
const handleMessage = response => {
const onMessage = response => {
if (client.readyState !== READY_STATE.OPEN_STATE) {
return;
}
client.send(JSON.stringify(response));
};
source$.subscribe(handleMessage);
const onError = err => this.baseExceptionFilter.handleError(client, err);
source$.subscribe(onMessage, onError);
}
public bindMessageHandler(

View File

@@ -7,7 +7,7 @@ import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-con
import { InterceptorsContextCreator } from '@nestjs/core/interceptors/interceptors-context-creator';
import { PipesConsumer } from '@nestjs/core/pipes/pipes-consumer';
import { PipesContextCreator } from '@nestjs/core/pipes/pipes-context-creator';
import { WsException } from '../exceptions/ws-exception';
import { WsException } from '../errors/ws-exception';
import { ExceptionFiltersContext } from './exception-filters-context';
import { WsProxy } from './ws-proxy';

View File

@@ -0,0 +1 @@
export * from './ws-exception';

View File

@@ -1,7 +1,7 @@
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
export class InvalidSocketPortException extends RuntimeException {
constructor(port, type) {
super(`Invalid port (${port}) in Gateway ${type}!`);
constructor(port: number | string, type: any) {
super(`Invalid port (${port}) in gateway ${type}!`);
}
}

View File

@@ -1,5 +1,6 @@
export class WsException extends Error {
public readonly message: any;
constructor(private readonly error: string | object) {
super();
this.message = error;

View File

@@ -1,16 +1,25 @@
import { ArgumentsHost, WsExceptionFilter } from '@nestjs/common';
import { isObject } from '@nestjs/common/utils/shared.utils';
import { MESSAGES } from '@nestjs/core/constants';
import { WsException } from './ws-exception';
import { WsException } from '../errors/ws-exception';
export class BaseWsExceptionFilter<T = any> implements WsExceptionFilter<T> {
catch(exception: T, host: ArgumentsHost) {
const client = host.switchToWs().getClient();
this.handleError(client, exception);
}
handleError<IClient extends { emit: Function }>(
client: IClient,
exception: T,
) {
const status = 'error';
if (!(exception instanceof WsException)) {
const errorMessage = MESSAGES.UNKNOWN_EXCEPTION_MESSAGE;
return client.emit('exception', { status, message: errorMessage });
return client.emit('exception', {
status,
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
});
}
const result = exception.getError();
const message = isObject(result)
@@ -19,6 +28,7 @@ export class BaseWsExceptionFilter<T = any> implements WsExceptionFilter<T> {
status,
message: result,
};
client.emit('exception', message);
}
}

View File

@@ -1,2 +1 @@
export * from './base-ws-exception-filter';
export * from './ws-exception';

View File

@@ -2,7 +2,7 @@ import { ArgumentsHost } from '@nestjs/common';
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface';
import { isEmpty } from '@nestjs/common/utils/shared.utils';
import { InvalidExceptionFilterException } from '@nestjs/core/errors/exceptions/invalid-exception-filter.exception';
import { WsException } from '../exceptions/ws-exception';
import { WsException } from '../errors/ws-exception';
import { BaseWsExceptionFilter } from './base-ws-exception-filter';
export class WsExceptionsHandler extends BaseWsExceptionFilter {
@@ -23,7 +23,10 @@ export class WsExceptionsHandler extends BaseWsExceptionFilter {
this.filters = filters;
}
public invokeCustomFilters(exception, args: ArgumentsHost): boolean {
public invokeCustomFilters<T = any>(
exception: T,
args: ArgumentsHost,
): boolean {
if (isEmpty(this.filters)) return false;
const filter = this.filters.find(({ exceptionMetatypes }) => {

View File

@@ -8,6 +8,7 @@ import 'reflect-metadata';
export * from './adapters/io-adapter';
export * from './adapters/ws-adapter';
export * from './errors';
export * from './exceptions';
export { MessageMappingProperties } from './gateway-metadata-explorer';
export * from './interfaces';

View File

@@ -22,7 +22,7 @@ export class SocketModule {
private applicationConfig: ApplicationConfig;
private webSocketsController: WebSocketsController;
public register(container, config) {
public register<TContainer, TConfig>(container: any, config: any) {
this.applicationConfig = config;
this.webSocketsController = new WebSocketsController(
new SocketServerProvider(this.socketsContainer, config),
@@ -30,16 +30,16 @@ export class SocketModule {
this.getContextCreator(container),
);
const modules = container.getModules();
modules.forEach(({ components }, moduleName) =>
this.hookGatewaysIntoServers(components, moduleName),
modules.forEach(({ providers }, moduleName) =>
this.hookGatewaysIntoServers(providers, moduleName),
);
}
public hookGatewaysIntoServers(
components: Map<string, InstanceWrapper<Injectable>>,
providers: Map<string, InstanceWrapper<Injectable>>,
moduleName: string,
) {
components.forEach(wrapper =>
providers.forEach(wrapper =>
this.hookGatewayIntoServer(wrapper, moduleName),
);
}

View File

@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { of } from 'rxjs';
import * as sinon from 'sinon';
import { Guard, Injectable, UseGuards, UsePipes } from '../../../common';
import { GuardsConsumer } from '../../../core/guards/guards-consumer';
import { GuardsContextCreator } from '../../../core/guards/guards-context-creator';
import { NestContainer } from '../../../core/injector/container';
@@ -8,17 +9,10 @@ import { InterceptorsConsumer } from '../../../core/interceptors/interceptors-co
import { InterceptorsContextCreator } from '../../../core/interceptors/interceptors-context-creator';
import { PipesConsumer } from '../../../core/pipes/pipes-consumer';
import { PipesContextCreator } from '../../../core/pipes/pipes-context-creator';
import { WsException } from '../../index';
import {
Component,
Guard,
Injectable,
UseGuards,
UsePipes,
} from '../../../common';
import { ExceptionFiltersContext } from '../../context/exception-filters-context';
import { WsContextCreator } from '../../context/ws-context-creator';
import { WsProxy } from '../../context/ws-proxy';
import { WsException } from '../../index';
@Guard()
class TestGuard {
@@ -44,7 +38,7 @@ describe('WsContextCreator', () => {
let module: string;
@UseGuards(TestGuard)
@Component()
@Injectable()
class Test {
@UsePipes(new TestPipe())
test(client: string, data: number) {

View File

@@ -2,7 +2,7 @@ import * as sinon from 'sinon';
import { expect } from 'chai';
import { WsProxy } from '../../context/ws-proxy';
import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler';
import { WsException } from '../../exceptions/ws-exception';
import { WsException } from '../../errors/ws-exception';
describe('WsProxy', () => {
let routerProxy: WsProxy;

View File

@@ -1,6 +1,6 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { WsException } from '../../exceptions/ws-exception';
import { WsException } from '../../errors/ws-exception';
describe('WsException', () => {
let instance: WsException;

View File

@@ -1,7 +1,7 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler';
import { WsException } from '../../exceptions/ws-exception';
import { WsException } from '../../errors/ws-exception';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
describe('WsExceptionsHandler', () => {

View File

@@ -5,7 +5,7 @@ import * as sinon from 'sinon';
import { MetadataScanner } from '../../core/metadata-scanner';
import { PORT_METADATA } from '../constants';
import { WsContextCreator } from '../context/ws-context-creator';
import { InvalidSocketPortException } from '../exceptions/invalid-socket-port.exception';
import { InvalidSocketPortException } from '../errors/invalid-socket-port.exception';
import { GatewayMetadataExplorer } from '../gateway-metadata-explorer';
import { IoAdapter } from '../index';
import { SocketServerProvider } from '../socket-server-provider';

View File

@@ -6,7 +6,7 @@ import { from as fromPromise, Observable, of, Subject } from 'rxjs';
import { distinctUntilChanged, mergeAll } from 'rxjs/operators';
import { GATEWAY_OPTIONS, PORT_METADATA } from './constants';
import { WsContextCreator } from './context/ws-context-creator';
import { InvalidSocketPortException } from './exceptions/invalid-socket-port.exception';
import { InvalidSocketPortException } from './errors/invalid-socket-port.exception';
import {
GatewayMetadataExplorer,
MessageMappingProperties,