chore(): resolve merge conflicts

This commit is contained in:
Kamil Myśliwiec
2021-01-29 13:51:00 +01:00
232 changed files with 4246 additions and 58794 deletions

View File

@@ -33,6 +33,7 @@ describe('GraphQL Pipes', () => {
code: 'INTERNAL_SERVER_ERROR',
exception: {
message: 'Bad Request Exception',
name: 'BadRequestException',
response: {
message: [
'description must be longer than or equal to 30 characters',

View File

@@ -0,0 +1,77 @@
import {
Controller,
Get,
INestApplication,
MiddlewareConsumer,
Module,
} from '@nestjs/common';
import { RouterModule } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { ApplicationModule } from '../src/app.module';
const RETURN_VALUE = 'test';
const SCOPED_VALUE = 'test_scoped';
@Controller()
class TestController {
@Get('test')
test() {
return RETURN_VALUE;
}
@Get('test2')
test2() {
return RETURN_VALUE;
}
}
@Module({
imports: [ApplicationModule],
controllers: [TestController],
})
class TestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => res.send(SCOPED_VALUE))
.forRoutes(TestController);
}
}
describe('RouterModule with Middleware functions', () => {
let app: INestApplication;
beforeEach(async () => {
app = (
await Test.createTestingModule({
imports: [
TestModule,
RouterModule.register([
{
path: '/module-path/',
module: TestModule,
},
]),
],
}).compile()
).createNestApplication();
await app.init();
});
it(`forRoutes(TestController) - /test`, () => {
return request(app.getHttpServer())
.get('/module-path/test')
.expect(200, SCOPED_VALUE);
});
it(`forRoutes(TestController) - /test2`, () => {
return request(app.getHttpServer())
.get('/module-path/test2')
.expect(200, SCOPED_VALUE);
});
afterEach(async () => {
await app.close();
});
});

View File

@@ -0,0 +1,99 @@
import { Controller, Get, INestApplication, Module } from '@nestjs/common';
import { RouterModule, Routes } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
describe('RouterModule', () => {
let app: INestApplication;
abstract class BaseController {
@Get()
getName() {
return this.constructor.name;
}
}
@Controller('/parent-controller')
class ParentController extends BaseController {}
@Controller('/child-controller')
class ChildController extends BaseController {}
@Controller('no-slash-controller')
class NoSlashController extends BaseController {}
class UnknownController {}
@Module({ controllers: [ParentController] })
class ParentModule {}
@Module({ controllers: [ChildController] })
class ChildModule {}
@Module({})
class AuthModule {}
@Module({})
class PaymentsModule {}
@Module({ controllers: [NoSlashController] })
class NoSlashModule {}
const routes1: Routes = [
{
path: 'parent',
module: ParentModule,
children: [
{
path: 'child',
module: ChildModule,
},
],
},
];
const routes2: Routes = [
{ path: 'v1', children: [AuthModule, PaymentsModule, NoSlashModule] },
];
@Module({
imports: [ParentModule, ChildModule, RouterModule.register(routes1)],
})
class MainModule {}
@Module({
imports: [
AuthModule,
PaymentsModule,
NoSlashModule,
RouterModule.register(routes2),
],
})
class AppModule {}
before(async () => {
const moduleRef = await Test.createTestingModule({
imports: [MainModule, AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('should hit the "ParentController"', async () => {
return request(app.getHttpServer())
.get('/parent/parent-controller')
.expect(200, 'ParentController');
});
it('should hit the "ChildController"', async () => {
return request(app.getHttpServer())
.get('/parent/child/child-controller')
.expect(200, 'ChildController');
});
it('should hit the "NoSlashController"', async () => {
return request(app.getHttpServer())
.get('/v1/no-slash-controller')
.expect(200, 'NoSlashController');
});
afterEach(async () => {
await app.close();
});
});

View File

@@ -1,4 +1,4 @@
import { BeforeApplicationShutdown, Injectable } from '@nestjs/common';
import { BeforeApplicationShutdown, Injectable, Module } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import * as Sinon from 'sinon';
@@ -19,35 +19,46 @@ describe('BeforeApplicationShutdown', () => {
const instance = module.get(TestInjectable);
expect(instance.beforeApplicationShutdown.called).to.be.true;
});
/*
it('should not stop the server once beforeApplicationShutdown has been called', async () => {
let resolve;
const promise = new Promise(r => (resolve = r));
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class BB implements BeforeApplicationShutdown {
public field: string;
async beforeApplicationShutdown() {
this.field = 'b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements BeforeApplicationShutdown {
public field: string;
constructor(private bb: BB) {}
async beforeApplicationShutdown() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
const module = await Test.createTestingModule({
providers: [
{
provide: 'Test',
useValue: {
beforeApplicationShutdown: () => promise,
},
},
],
imports: [A],
}).compile();
Sinon.stub(module, 'dispose' as any);
const app = module.createNestApplication();
await app.init();
await app.close();
app.close();
expect(((module as any).dispose as Sinon.SinonSpy).called, 'dispose').to.be
.false;
resolve();
setTimeout(
() =>
expect(((module as any).dispose as Sinon.SinonSpy).called, 'dispose').to
.be.true,
0,
);
});*/
const instance = module.get(AA);
expect(instance.field).to.equal('b-field_a-field');
});
});

View File

@@ -1,7 +1,7 @@
import { Injectable, Module, OnApplicationBootstrap } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import * as Sinon from 'sinon';
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
@Injectable()
class TestInjectable implements OnApplicationBootstrap {
@@ -41,4 +41,45 @@ describe('OnApplicationBootstrap', () => {
const app = module.createNestApplication();
await app.init().then(obj => expect(obj).to.not.be.undefined);
});
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class BB implements OnApplicationBootstrap {
public field: string;
async onApplicationBootstrap() {
this.field = 'b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements OnApplicationBootstrap {
public field: string;
constructor(private bb: BB) {}
async onApplicationBootstrap() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
const module = await Test.createTestingModule({
imports: [A],
}).compile();
const app = module.createNestApplication();
await app.init();
const instance = module.get(AA);
expect(instance.field).to.equal('b-field_a-field');
});
});

View File

@@ -1,4 +1,4 @@
import { Injectable, OnApplicationShutdown } from '@nestjs/common';
import { Injectable, Module, OnApplicationShutdown } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import * as Sinon from 'sinon';
@@ -19,4 +19,46 @@ describe('OnApplicationShutdown', () => {
const instance = module.get(TestInjectable);
expect(instance.onApplicationShutdown.called).to.be.true;
});
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class BB implements OnApplicationShutdown {
public field: string;
async onApplicationShutdown() {
this.field = 'b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements OnApplicationShutdown {
public field: string;
constructor(private bb: BB) {}
async onApplicationShutdown() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
const module = await Test.createTestingModule({
imports: [A],
}).compile();
const app = module.createNestApplication();
await app.init();
await app.close();
const instance = module.get(AA);
expect(instance.field).to.equal('b-field_a-field');
});
});

View File

@@ -1,4 +1,4 @@
import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { Injectable, Module, OnModuleDestroy } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import * as Sinon from 'sinon';
@@ -39,4 +39,46 @@ describe('OnModuleDestroy', () => {
const app = module.createNestApplication();
await app.init().then(obj => expect(obj).to.not.be.undefined);
});
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class BB implements OnModuleDestroy {
public field: string;
async onModuleDestroy() {
this.field = 'b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements OnModuleDestroy {
public field: string;
constructor(private bb: BB) {}
async onModuleDestroy() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
const module = await Test.createTestingModule({
imports: [A],
}).compile();
const app = module.createNestApplication();
await app.init();
await app.close();
const instance = module.get(AA);
expect(instance.field).to.equal('b-field_a-field');
});
});

View File

@@ -1,7 +1,7 @@
import { Injectable, Module, OnModuleInit } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import * as Sinon from 'sinon';
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
class TestInjectable implements OnModuleInit {
@@ -37,4 +37,45 @@ describe('OnModuleInit', () => {
const app = module.createNestApplication();
await app.init().then(obj => expect(obj).to.not.be.undefined);
});
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class BB implements OnModuleInit {
public field: string;
async onModuleInit() {
this.field = 'b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements OnModuleInit {
public field: string;
constructor(private bb: BB) {}
async onModuleInit() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
const module = await Test.createTestingModule({
imports: [A],
}).compile();
const app = module.createNestApplication();
await app.init();
const instance = module.get(AA);
expect(instance.field).to.equal('b-field_a-field');
});
});

View File

@@ -28,7 +28,7 @@ describe('MQTT transport', () => {
host: '0.0.0.0',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -28,7 +28,7 @@ describe('NATS transport', () => {
url: 'nats://0.0.0.0:4222',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -28,7 +28,7 @@ describe('REDIS transport', () => {
url: 'redis://0.0.0.0:6379',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -57,7 +57,7 @@ describe('Kafka concurrent', function () {
apps.push(app);
// await the start
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
};

View File

@@ -1,3 +1,4 @@
import * as GRPC from '@grpc/grpc-js';
import * as ProtoLoader from '@grpc/proto-loader';
import { INestApplication } from '@nestjs/common';
import { Transport } from '@nestjs/microservices';
@@ -6,7 +7,6 @@ import { Test } from '@nestjs/testing';
import { fail } from 'assert';
import { expect } from 'chai';
import * as express from 'express';
import * as GRPC from 'grpc';
import { join } from 'path';
import * as request from 'supertest';
import { AdvancedGrpcController } from '../src/grpc-advanced/advanced.grpc.controller';
@@ -39,7 +39,7 @@ describe('Advanced GRPC transport', () => {
},
});
// Start gRPC microservice
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
// Load proto-buffers for test gRPC dispatch
const proto = ProtoLoader.loadSync('root.proto', {

View File

@@ -1,10 +1,10 @@
import * as GRPC from '@grpc/grpc-js';
import * as ProtoLoader from '@grpc/proto-loader';
import { INestApplication } from '@nestjs/common';
import { Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import { fail } from 'assert';
import { expect } from 'chai';
import * as GRPC from 'grpc';
import { join } from 'path';
import * as request from 'supertest';
import { GrpcController } from '../src/grpc/grpc.controller';
@@ -33,7 +33,7 @@ describe('GRPC transport', () => {
},
});
// Start gRPC microservice
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
// Load proto-buffers for test gRPC dispatch
const proto = ProtoLoader.loadSync(

View File

@@ -14,7 +14,7 @@ describe('Kafka transport', function () {
let app: INestApplication;
// set timeout to be longer (especially for the after hook)
this.timeout(30000);
this.timeout(50000);
it(`Start Kafka app`, async () => {
const module = await Test.createTestingModule({
@@ -33,7 +33,7 @@ describe('Kafka transport', function () {
},
});
app.enableShutdownHooks();
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
}).timeout(30000);
@@ -130,4 +130,4 @@ describe('Kafka transport', function () {
after(`Stopping Kafka app`, async () => {
await app.close();
});
}).timeout(30000);
}).timeout(50000);

View File

@@ -23,7 +23,7 @@ describe('MQTT transport', () => {
url: 'mqtt://0.0.0.0:1883',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -23,7 +23,7 @@ describe('NATS transport', () => {
url: 'nats://0.0.0.0:4222',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -23,7 +23,7 @@ describe('REDIS transport', () => {
url: 'redis://0.0.0.0:6379',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -26,7 +26,7 @@ describe('RabbitMQ transport', () => {
socketOptions: { noDelay: true },
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -24,7 +24,7 @@ describe('RPC transport', () => {
host: '0.0.0.0',
},
});
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
await app.init();
});

View File

@@ -1,3 +1,4 @@
import { Metadata } from '@grpc/grpc-js';
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
import {
Client,
@@ -9,7 +10,6 @@ import {
} from '@nestjs/microservices';
import { join } from 'path';
import { Observable, of, ReplaySubject, Subject } from 'rxjs';
import { Metadata } from 'grpc';
@Controller()
export class AdvancedGrpcController {

View File

@@ -0,0 +1,47 @@
import { ExpressAdapter } from '@nestjs/platform-express';
import { Test, TestingModule } from '@nestjs/testing';
import { expect } from 'chai';
import * as express from 'express';
import { AppModule } from '../src/app.module';
import { INestApplication } from '@nestjs/common';
describe('Listen (Express Application)', () => {
let testModule: TestingModule;
let app: INestApplication;
beforeEach(async () => {
testModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = testModule.createNestApplication(new ExpressAdapter(express()));
});
afterEach(async () => {
app.close();
});
it('should resolve with httpServer on success', async () => {
const response = await app.listen(3000);
expect(response).to.eql(app.getHttpServer());
});
it('should reject if the port is not available', async () => {
await app.listen(3000);
const secondApp = testModule.createNestApplication(
new ExpressAdapter(express()),
);
try {
await secondApp.listen(3000);
} catch (error) {
expect(error.code).to.equal('EADDRINUSE');
}
});
it('should reject if there is an invalid host', async () => {
try {
await app.listen(3000, '1');
} catch (error) {
expect(error.code).to.equal('EADDRNOTAVAIL');
}
});
});

View File

@@ -0,0 +1,46 @@
import { FastifyAdapter } from '@nestjs/platform-fastify';
import { Test, TestingModule } from '@nestjs/testing';
import { expect } from 'chai';
import { AppModule } from '../src/app.module';
import { INestApplication } from '@nestjs/common';
describe('Listen (Fastify Application)', () => {
let testModule: TestingModule;
let app: INestApplication;
beforeEach(async () => {
testModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = testModule.createNestApplication(new FastifyAdapter());
});
afterEach(async () => {
app.close();
});
it('should resolve with httpServer on success', async () => {
const response = await app.listen(3000);
expect(response).to.eql(app.getHttpServer());
});
it('should reject if the port is not available', async () => {
await app.listen(3000);
const secondApp = testModule.createNestApplication(new FastifyAdapter());
try {
await secondApp.listen(3000);
} catch (error) {
expect(error.code).to.equal('EADDRINUSE');
}
await secondApp.close();
});
it('should reject if there is an invalid host', async () => {
try {
await app.listen(3000, '1');
} catch (error) {
expect(error.code).to.equal('EADDRNOTAVAIL');
}
});
});

View File

@@ -0,0 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.sayHello();
}
}

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
sayHello(): string {
return 'Hello World!';
}
}

View File

@@ -35,7 +35,7 @@ describe('Request scope (microservices)', () => {
server = app.getHttpServer();
await app.init();
await app.startAllMicroservicesAsync();
await app.startAllMicroservices();
});
describe('when one service is request scoped', () => {

View File

@@ -12,7 +12,7 @@ describe('ErrorGateway', () => {
providers: [ErrorGateway],
}).compile();
app = await testingModule.createNestApplication();
await app.listenAsync(3000);
await app.listen(3000);
});
it(`should handle error`, async () => {

View File

@@ -17,7 +17,7 @@ describe('WebSocketGateway (ack)', () => {
it(`should handle message with ack (http)`, async () => {
app = await createNestApp(AckGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = io.connect('http://localhost:8080');
await new Promise<void>(resolve =>
@@ -30,7 +30,7 @@ describe('WebSocketGateway (ack)', () => {
it(`should handle message with ack & without data (http)`, async () => {
app = await createNestApp(AckGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = io.connect('http://localhost:8080');
await new Promise<void>(resolve =>

View File

@@ -19,7 +19,7 @@ describe('WebSocketGateway', () => {
it(`should handle message (2nd port)`, async () => {
app = await createNestApp(ApplicationGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = io.connect('http://localhost:8080');
ws.emit('push', {
@@ -35,7 +35,7 @@ describe('WebSocketGateway', () => {
it(`should handle message (http)`, async () => {
app = await createNestApp(ServerGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = io.connect('http://localhost:3000');
ws.emit('push', {
@@ -51,7 +51,7 @@ describe('WebSocketGateway', () => {
it(`should handle message (2 gateways)`, async () => {
app = await createNestApp(ApplicationGateway, NamespaceGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = io.connect('http://localhost:8080');
io.connect('http://localhost:8080/test').emit('push', {});

View File

@@ -21,7 +21,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
it(`should handle message (2nd port)`, async () => {
app = await createNestApp(ApplicationGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = new WebSocket('ws://localhost:8080');
await new Promise(resolve => ws.on('open', resolve));
@@ -44,7 +44,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
it(`should handle message (http)`, async () => {
app = await createNestApp(ServerGateway);
await app.listenAsync(3000);
await app.listen(3000);
ws = new WebSocket('ws://localhost:3000');
await new Promise(resolve => ws.on('open', resolve));
@@ -69,7 +69,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
this.retries(10);
app = await createNestApp(ApplicationGateway, CoreGateway);
await app.listenAsync(3000);
await app.listen(3000);
// open websockets delay
await new Promise(resolve => setTimeout(resolve, 1000));

View File

@@ -3,5 +3,5 @@
"packages": [
"packages/*"
],
"version": "7.6.6"
"version": "7.6.7"
}

34198
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
"coverage": "nyc report --reporter=text-lcov | coveralls",
"format": "prettier \"**/*.ts\" --ignore-path ./.prettierignore --write && git status",
"postinstall": "opencollective",
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --retries 3 --require 'node_modules/reflect-metadata/Reflect.js' --exit",
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --require 'node_modules/reflect-metadata/Reflect.js' --exit",
"test:integration": "mocha \"integration/*/{,!(node_modules)/**/}/*.spec.ts\" --reporter spec --require ts-node/register --require 'node_modules/reflect-metadata/Reflect.js' --exit",
"test:docker:up": "docker-compose -f integration/docker-compose.yml up -d",
"test:docker:down": "docker-compose -f integration/docker-compose.yml down",
@@ -74,6 +74,7 @@
"@codechecks/client": "0.1.10",
"@commitlint/cli": "11.0.0",
"@commitlint/config-angular": "11.0.0",
"@grpc/grpc-js": "1.1.1",
"@grpc/proto-loader": "0.5.6",
"@nestjs/graphql": "7.9.8",
"@nestjs/mongoose": "7.2.2",
@@ -86,6 +87,7 @@
"@types/cors": "2.8.9",
"@types/express": "4.17.11",
"@types/gulp": "4.0.8",
"@types/http-errors": "1.8.0",
"@types/mocha": "8.2.0",
"@types/mongoose": "5.10.3",
"@types/node": "14.14.22",
@@ -126,13 +128,13 @@
"fastify-static": "3.4.0",
"graphql": "15.5.0",
"graphql-tools": "7.0.2",
"grpc": "1.24.4",
"gulp": "4.0.2",
"gulp-clang-format": "1.0.27",
"gulp-clean": "0.4.0",
"gulp-sourcemaps": "3.0.0",
"gulp-typescript": "5.0.1",
"gulp-watch": "5.0.1",
"http-errors": "1.8.0",
"husky": "4.3.8",
"imports-loader": "1.2.0",
"json-loader": "0.5.7",
@@ -151,7 +153,7 @@
"nats": "1.4.12",
"nodemon": "2.0.7",
"nyc": "15.1.0",
"point-of-view": "4.8.0",
"point-of-view": "4.9.0",
"prettier": "2.2.1",
"redis": "3.0.2",
"rxjs-compat": "6.6.3",
@@ -182,7 +184,6 @@
"packages/**/*.ts"
],
"exclude": [
"node_modules/",
"packages/**/test/**",
"packages/**/*.spec.ts",
"packages/**/adapters/*.ts",
@@ -199,7 +200,7 @@
"packages/core/injector/instance-links-host.ts",
"packages/common/cache/**/*",
"packages/common/serializer/**/*",
"packages/common/services/logger.service.ts"
"packages/common/services/*.ts"
],
"extension": [
".ts"

View File

@@ -2,7 +2,7 @@ import {
PROPERTY_DEPS_METADATA,
SELF_DECLARED_DEPS_METADATA,
} from '../../constants';
import { isFunction, isUndefined } from '../../utils/shared.utils';
import { isUndefined } from '../../utils/shared.utils';
/**
* Decorator that marks a constructor parameter as a target for
@@ -35,9 +35,7 @@ import { isFunction, isUndefined } from '../../utils/shared.utils';
*/
export function Inject<T = any>(token?: T) {
return (target: object, key: string | symbol, index?: number) => {
token = token || Reflect.getMetadata('design:type', target, key);
const type =
token && isFunction(token) ? ((token as any) as Function).name : token;
const type = token || Reflect.getMetadata('design:type', target, key);
if (!isUndefined(index)) {
let dependencies =

View File

@@ -46,7 +46,7 @@ export function Injectable(options?: InjectableOptions): ClassDecorator {
};
}
export function mixin(mixinClass: Type<any>) {
export function mixin<T>(mixinClass: Type<T>) {
Object.defineProperty(mixinClass, 'name', {
value: uuid(),
});

View File

@@ -40,6 +40,7 @@ export class HttpException extends Error {
) {
super();
this.initMessage();
this.initName();
}
public initMessage() {
@@ -57,6 +58,10 @@ export class HttpException extends Error {
}
}
public initName(): void {
this.name = this.constructor.name;
}
public getResponse(): string | object {
return this.response;
}

View File

@@ -41,6 +41,8 @@ export interface HttpServer<TRequest = any, TResponse = any> {
put(path: string, handler: RequestHandler<TRequest, TResponse>): any;
patch(handler: RequestHandler<TRequest, TResponse>): any;
patch(path: string, handler: RequestHandler<TRequest, TResponse>): any;
all(path: string, handler: RequestHandler<TRequest, TResponse>): any;
all(handler: RequestHandler<TRequest, TResponse>): any;
options(handler: RequestHandler<TRequest, TResponse>): any;
options(path: string, handler: RequestHandler<TRequest, TResponse>): any;
listen(port: number | string, callback?: () => void): any;

View File

@@ -13,6 +13,20 @@ export class NestApplicationContextOptions {
* Whether to abort the process on Error. By default, the process is exited.
* Pass `false` to override the default behavior. If `false` is passed, Nest will not exit
* the application and instead will rethrow the exception.
* @default true
*/
abortOnError?: boolean | undefined;
/**
* If enabled, logs will be buffered until the "Logger#flush" method is called.
* @default false
*/
bufferLogs?: boolean;
/**
* If enabled, logs will be automatically flushed and buffer detached when
* application initialisation process either completes or fails.
* @default true
*/
autoFlushLogs?: boolean;
}

View File

@@ -56,6 +56,12 @@ export interface INestApplicationContext {
*/
useLogger(logger: LoggerService | LogLevel[] | false): void;
/**
* Prints buffered logs and detaches buffer.
* @returns {void}
*/
flushLogs(): void;
/**
* Enables the usage of shutdown hooks. Will call the
* `onApplicationShutdown` function of a provider if the

View File

@@ -50,15 +50,9 @@ export interface INestApplication extends INestApplicationContext {
callback?: () => void,
): Promise<any>;
/**
* Returns the url the application is listening at, based on OS and IP version. Returns as an IP value either in IPv6 or IPv4
*
* @returns {Promise<string>} The IP where the server is listening
*/
getUrl(): Promise<string>;
/**
* Starts the application (can be awaited).
* @deprecated use "listen" instead.
*
* @param {number|string} port
* @param {string} [hostname]
@@ -66,6 +60,13 @@ export interface INestApplication extends INestApplicationContext {
*/
listenAsync(port: number | string, hostname?: string): Promise<any>;
/**
* Returns the url the application is listening at, based on OS and IP version. Returns as an IP value either in IPv6 or IPv4
*
* @returns {Promise<string>} The IP where the server is listening
*/
getUrl(): Promise<string>;
/**
* Registers a prefix for every HTTP route path.
*
@@ -121,17 +122,17 @@ export interface INestApplication extends INestApplicationContext {
/**
* Starts all connected microservices asynchronously.
*
* @param {Function} [callback] Optional callback function
* @returns {this}
* @returns {Promise}
*/
startAllMicroservices(callback?: () => void): this;
startAllMicroservices(): Promise<this>;
/**
* Starts all connected microservices and can be awaited.
* @deprecated use "startAllMicroservices" instead.
*
* @returns {Promise}
*/
startAllMicroservicesAsync(): Promise<void>;
startAllMicroservicesAsync(): Promise<this>;
/**
* Registers exception filters as global filters (will be used within

View File

@@ -14,13 +14,13 @@ export interface INestMicroservice extends INestApplicationContext {
/**
* Starts the microservice.
*
* @param {Function} callback
* @returns {void}
*/
listen(callback: () => void): void;
listen(): Promise<any>;
/**
* Starts the microservice (can be awaited).
* @deprecated use "listen" instead.
*
* @returns {Promise}
*/

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/common",
"version": "7.6.6",
"version": "7.6.7",
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
"author": "Kamil Mysliwiec",
"homepage": "https://nestjs.com",

View File

@@ -0,0 +1,254 @@
import { Injectable } from '../decorators/core/injectable.decorator';
import { Optional } from '../decorators/core/optional.decorator';
import { clc, yellow } from '../utils/cli-colors.util';
import { isPlainObject } from '../utils/shared.utils';
import { LoggerService, LogLevel } from './logger.service';
import { isLogLevelEnabled } from './utils';
export interface ConsoleLoggerOptions {
/**
* Enabled log levels.
*/
logLevels?: LogLevel[];
/**
* If enabled, will print timestamp (time difference) between current and previous log message.
*/
timestamp?: boolean;
}
const DEFAULT_LOG_LEVELS: LogLevel[] = [
'log',
'error',
'warn',
'debug',
'verbose',
];
@Injectable()
export class ConsoleLogger implements LoggerService {
private static lastTimestampAt?: number;
constructor();
constructor(context: string);
constructor(context: string, options: ConsoleLoggerOptions);
constructor(
@Optional()
protected context?: string,
@Optional()
protected options: ConsoleLoggerOptions = {},
) {
if (!options.logLevels) {
options.logLevels = DEFAULT_LOG_LEVELS;
}
}
/**
* Write a 'log' level log, if the configured level allows for it.
* Prints to `stdout` with newline.
*/
log(message: any, context?: string): void;
log(message: any, ...optionalParams: [...any, string]): void;
log(message: any, ...optionalParams: [...any, string]) {
if (!this.isLevelEnabled('log')) {
return;
}
const { messages, context } = this.getContextAndMessagesToPrint([
message,
...optionalParams,
]);
this.printMessages(messages, context, 'log');
}
/**
* Write an 'error' level log, if the configured level allows for it.
* Prints to `stderr` with newline.
*/
error(message: any, stack?: string, context?: string): void;
error(message: any, ...optionalParams: [...any, string, string]): void;
error(message: any, ...optionalParams: [...any, string, string]) {
if (!this.isLevelEnabled('error')) {
return;
}
const {
messages,
context,
stack,
} = this.getContextAndStackAndMessagesToPrint([message, ...optionalParams]);
this.printMessages(messages, context, 'error', 'stderr');
this.printStackTrace(stack);
}
/**
* Write a 'warn' level log, if the configured level allows for it.
* Prints to `stdout` with newline.
*/
warn(message: any, context?: string): void;
warn(message: any, ...optionalParams: [...any, string]): void;
warn(message: any, ...optionalParams: [...any, string]) {
if (!this.isLevelEnabled('warn')) {
return;
}
const { messages, context } = this.getContextAndMessagesToPrint([
message,
...optionalParams,
]);
this.printMessages(messages, context, 'warn');
}
/**
* Write a 'debug' level log, if the configured level allows for it.
* Prints to `stdout` with newline.
*/
debug(message: any, context?: string): void;
debug(message: any, ...optionalParams: [...any, string]): void;
debug(message: any, ...optionalParams: [...any, string]) {
if (!this.isLevelEnabled('debug')) {
return;
}
const { messages, context } = this.getContextAndMessagesToPrint([
message,
...optionalParams,
]);
this.printMessages(messages, context, 'debug');
}
/**
* Write a 'verbose' level log, if the configured level allows for it.
* Prints to `stdout` with newline.
*/
verbose(message: any, context?: string): void;
verbose(message: any, ...optionalParams: [...any, string]): void;
verbose(message: any, ...optionalParams: [...any, string]) {
if (!this.isLevelEnabled('verbose')) {
return;
}
const { messages, context } = this.getContextAndMessagesToPrint([
message,
...optionalParams,
]);
this.printMessages(messages, context, 'verbose');
}
/**
* Set log levels
* @param levels log levels
*/
setLogLevels(levels: LogLevel[]) {
if (!this.options) {
this.options = {};
}
this.options.logLevels = levels;
}
/**
* Set logger context
* @param context context
*/
setContext(context: string) {
this.context = context;
}
isLevelEnabled(level: LogLevel): boolean {
const logLevels = this.options?.logLevels;
return isLogLevelEnabled(level, logLevels);
}
protected getTimestamp(): string {
const localeStringOptions = {
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
day: '2-digit',
month: '2-digit',
};
return new Date(Date.now()).toLocaleString(undefined, localeStringOptions);
}
protected printMessages(
messages: unknown[],
context = '',
logLevel: LogLevel = 'log',
writeStreamType?: 'stdout' | 'stderr',
) {
const color = this.getColorByLogLevel(logLevel);
messages.forEach(message => {
const output = isPlainObject(message)
? `${color('Object:')}\n${JSON.stringify(message, null, 2)}\n`
: color(message as string);
const pidMessage = color(`[Nest] ${process.pid} - `);
const contextMessage = context ? yellow(`[${context}] `) : '';
const timestampDiff = this.updateAndGetTimestampDiff();
const formattedLogLevel = color(logLevel.toUpperCase().padStart(7, ' '));
const computedMessage = `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${timestampDiff}\n`;
process[writeStreamType ?? 'stdout'].write(computedMessage);
});
}
protected printStackTrace(stack: string) {
if (!stack) {
return;
}
process.stderr.write(`${stack}\n`);
}
private updateAndGetTimestampDiff(): string {
const includeTimestamp =
ConsoleLogger.lastTimestampAt && this.options?.timestamp;
const result = includeTimestamp
? yellow(` +${Date.now() - ConsoleLogger.lastTimestampAt}ms`)
: '';
ConsoleLogger.lastTimestampAt = Date.now();
return result;
}
private getContextAndMessagesToPrint(args: unknown[]) {
if (args?.length <= 1) {
return { messages: args, context: this.context };
}
const lastElement = args[args.length - 1];
const isContext = typeof lastElement === 'string';
if (!isContext) {
return { messages: args, context: this.context };
}
return {
context: lastElement as string,
messages: args.slice(0, args.length - 1),
};
}
private getContextAndStackAndMessagesToPrint(args: unknown[]) {
const { messages, context } = this.getContextAndMessagesToPrint(args);
if (messages?.length <= 1) {
return { messages, context };
}
const lastElement = messages[messages.length - 1];
const isStack = typeof lastElement === 'string';
if (!isStack) {
return { messages, context };
}
return {
stack: lastElement as string,
messages: messages.slice(0, messages.length - 1),
context,
};
}
private getColorByLogLevel(level: LogLevel) {
switch (level) {
case 'debug':
return clc.magentaBright;
case 'warn':
return clc.yellow;
case 'error':
return clc.red;
case 'verbose':
return clc.cyanBright;
default:
return clc.green;
}
}
}

View File

@@ -1 +1,2 @@
export * from './console-logger.service';
export * from './logger.service';

View File

@@ -1,179 +1,264 @@
import { Injectable } from '../decorators/core/injectable.decorator';
import { Optional } from '../decorators/core/optional.decorator';
import { clc, yellow } from '../utils/cli-colors.util';
import { isObject, isPlainObject } from '../utils/shared.utils';
declare const process: any;
import { isObject } from '../utils/shared.utils';
import { ConsoleLogger } from './console-logger.service';
import { isLogLevelEnabled } from './utils';
export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose';
export interface LoggerService {
log(message: any, context?: string);
error(message: any, trace?: string, context?: string);
warn(message: any, context?: string);
debug?(message: any, context?: string);
verbose?(message: any, context?: string);
/**
* Write a 'log' level log.
*/
log(message: any, ...optionalParams: any[]): any;
/**
* Write an 'error' level log.
*/
error(message: any, ...optionalParams: any[]): any;
/**
* Write a 'warn' level log.
*/
warn(message: any, ...optionalParams: any[]): any;
/**
* Write a 'debug' level log.
*/
debug?(message: any, ...optionalParams: any[]): any;
/**
* Write a 'verbose' level log.
*/
verbose?(message: any, ...optionalParams: any[]): any;
/**
* Set log levels.
* @param levels log levels
*/
setLogLevels?(levels: LogLevel[]): any;
}
interface LogBufferRecord {
/**
* Method to execute.
*/
methodRef: Function;
/**
* Arguments to pass to the method.
*/
arguments: unknown[];
}
const DEFAULT_LOGGER = new ConsoleLogger();
@Injectable()
export class Logger implements LoggerService {
private static logLevels: LogLevel[] = [
'log',
'error',
'warn',
'debug',
'verbose',
];
private static lastTimestamp?: number;
protected static instance?: typeof Logger | LoggerService = Logger;
protected static logBuffer = new Array<LogBufferRecord>();
protected static staticInstanceRef?: LoggerService = DEFAULT_LOGGER;
protected static logLevels?: LogLevel[];
private static isBufferAttached: boolean;
protected localInstanceRef?: LoggerService;
private static WrapBuffer: MethodDecorator = (
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<any>,
) => {
const originalFn = descriptor.value;
descriptor.value = function (...args: unknown[]) {
if (Logger.isBufferAttached) {
Logger.logBuffer.push({
methodRef: originalFn.bind(this),
arguments: args,
});
return;
}
return originalFn.call(this, ...args);
};
};
constructor();
constructor(context: string);
constructor(context: string, options?: { timestamp?: boolean });
constructor(
@Optional() protected context?: string,
@Optional() private readonly isTimestampEnabled = false,
@Optional() protected options: { timestamp?: boolean } = {},
) {}
error(message: any, trace = '', context?: string) {
const instance = this.getInstance();
if (!this.isLogLevelEnabled('error')) {
return;
get localInstance(): LoggerService {
if (Logger.staticInstanceRef === DEFAULT_LOGGER) {
if (this.localInstanceRef) {
return this.localInstanceRef;
}
this.localInstanceRef = new ConsoleLogger(this.context, {
timestamp: this.options?.timestamp,
logLevels: Logger.logLevels,
});
return this.localInstanceRef;
}
instance &&
instance.error.call(instance, message, trace, context || this.context);
return Logger.staticInstanceRef;
}
log(message: any, context?: string) {
this.callFunction('log', message, context);
/**
* Write an 'error' level log.
*/
error(message: any, stack?: string, context?: string): void;
error(message: any, ...optionalParams: [...any, string, string]): void;
@Logger.WrapBuffer
error(message: any, ...optionalParams: [...any, string, string]) {
optionalParams = this.context
? optionalParams.concat(this.context)
: optionalParams;
this.localInstance?.error(message, ...optionalParams);
}
warn(message: any, context?: string) {
this.callFunction('warn', message, context);
/**
* Write a 'log' level log.
*/
log(message: any, context?: string): void;
log(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
log(message: any, ...optionalParams: [...any, string]) {
optionalParams = this.context
? optionalParams.concat(this.context)
: optionalParams;
this.localInstance?.log(message, ...optionalParams);
}
debug(message: any, context?: string) {
this.callFunction('debug', message, context);
/**
* Write a 'warn' level log.
*/
warn(message: any, context?: string): void;
warn(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
warn(message: any, ...optionalParams: [...any, string]) {
optionalParams = this.context
? optionalParams.concat(this.context)
: optionalParams;
this.localInstance?.warn(message, ...optionalParams);
}
verbose(message: any, context?: string) {
this.callFunction('verbose', message, context);
/**
* Write a 'debug' level log.
*/
debug(message: any, context?: string): void;
debug(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
debug(message: any, ...optionalParams: [...any, string]) {
optionalParams = this.context
? optionalParams.concat(this.context)
: optionalParams;
this.localInstance?.debug(message, ...optionalParams);
}
setContext(context: string) {
this.context = context;
/**
* Write a 'verbose' level log.
*/
verbose(message: any, context?: string): void;
verbose(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
verbose(message: any, ...optionalParams: [...any, string]) {
optionalParams = this.context
? optionalParams.concat(this.context)
: optionalParams;
this.localInstance?.verbose(message, ...optionalParams);
}
getTimestamp() {
return Logger.getTimestamp();
/**
* Write an 'error' level log.
*/
static error(message: any, context?: string): void;
static error(message: any, stack?: string, context?: string): void;
static error(message: any, ...optionalParams: [...any, string, string]): void;
@Logger.WrapBuffer
static error(message: any, ...optionalParams: [...any, string, string]) {
this.staticInstanceRef?.error(message, ...optionalParams);
}
/**
* Write a 'log' level log.
*/
static log(message: any, context?: string): void;
static log(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
static log(message: any, ...optionalParams: [...any, string]) {
this.staticInstanceRef?.log(message, ...optionalParams);
}
/**
* Write a 'warn' level log.
*/
static warn(message: any, context?: string): void;
static warn(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
static warn(message: any, ...optionalParams: [...any, string]) {
this.staticInstanceRef?.warn(message, ...optionalParams);
}
/**
* Write a 'debug' level log, if the configured level allows for it.
* Prints to `stdout` with newline.
*/
static debug(message: any, context?: string): void;
static debug(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
static debug(message: any, ...optionalParams: [...any, string]) {
this.staticInstanceRef?.debug(message, ...optionalParams);
}
/**
* Write a 'verbose' level log.
*/
static verbose(message: any, context?: string): void;
static verbose(message: any, ...optionalParams: [...any, string]): void;
@Logger.WrapBuffer
static verbose(message: any, ...optionalParams: [...any, string]) {
this.staticInstanceRef?.verbose(message, ...optionalParams);
}
/**
* Print buffered logs and detach buffer.
*/
static flush() {
this.isBufferAttached = false;
this.logBuffer.forEach(item =>
item.methodRef(...(item.arguments as [string])),
);
this.logBuffer = [];
}
/**
* Attach buffer.
* Turns on initialisation logs buffering.
*/
static attachBuffer() {
this.isBufferAttached = true;
}
/**
* Detach buffer.
* Turns off initialisation logs buffering.
*/
static detachBuffer() {
this.isBufferAttached = false;
}
static overrideLogger(logger: LoggerService | LogLevel[] | boolean) {
if (Array.isArray(logger)) {
this.logLevels = logger;
return;
Logger.logLevels = logger;
return this.staticInstanceRef?.setLogLevels(logger);
}
this.instance = isObject(logger) ? (logger as LoggerService) : undefined;
this.staticInstanceRef = isObject(logger)
? (logger as LoggerService)
: undefined;
}
static log(message: any, context = '', isTimeDiffEnabled = true) {
this.printMessage(message, clc.green, context, isTimeDiffEnabled);
}
static error(
message: any,
trace = '',
context = '',
isTimeDiffEnabled = true,
) {
this.printMessage(message, clc.red, context, isTimeDiffEnabled, 'stderr');
this.printStackTrace(trace);
}
static warn(message: any, context = '', isTimeDiffEnabled = true) {
this.printMessage(message, clc.yellow, context, isTimeDiffEnabled);
}
static debug(message: any, context = '', isTimeDiffEnabled = true) {
this.printMessage(message, clc.magentaBright, context, isTimeDiffEnabled);
}
static verbose(message: any, context = '', isTimeDiffEnabled = true) {
this.printMessage(message, clc.cyanBright, context, isTimeDiffEnabled);
}
static getTimestamp() {
const localeStringOptions = {
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
day: '2-digit',
month: '2-digit',
};
return new Date(Date.now()).toLocaleString(undefined, localeStringOptions);
}
private callFunction(
name: 'log' | 'warn' | 'debug' | 'verbose',
message: any,
context?: string,
) {
if (!this.isLogLevelEnabled(name)) {
return;
}
const instance = this.getInstance();
const func = instance && (instance as typeof Logger)[name];
func &&
func.call(
instance,
message,
context || this.context,
this.isTimestampEnabled,
);
}
protected getInstance(): typeof Logger | LoggerService {
const { instance } = Logger;
return instance === this ? Logger : instance;
}
private isLogLevelEnabled(level: LogLevel): boolean {
return Logger.logLevels.includes(level);
}
private static printMessage(
message: any,
color: (message: string) => string,
context = '',
isTimeDiffEnabled?: boolean,
writeStreamType?: 'stdout' | 'stderr',
) {
const output = isPlainObject(message)
? `${color('Object:')}\n${JSON.stringify(message, null, 2)}\n`
: color(message);
const pidMessage = color(`[Nest] ${process.pid} - `);
const contextMessage = context ? yellow(`[${context}] `) : '';
const timestampDiff = this.updateAndGetTimestampDiff(isTimeDiffEnabled);
const instance = (this.instance as typeof Logger) ?? Logger;
const computedMessage = `${pidMessage}${instance.getTimestamp?.()} ${contextMessage}${output}${timestampDiff}\n`;
process[writeStreamType ?? 'stdout'].write(computedMessage);
}
private static updateAndGetTimestampDiff(
isTimeDiffEnabled?: boolean,
): string {
const includeTimestamp = Logger.lastTimestamp && isTimeDiffEnabled;
const result = includeTimestamp
? yellow(` +${Date.now() - Logger.lastTimestamp}ms`)
: '';
Logger.lastTimestamp = Date.now();
return result;
}
private static printStackTrace(trace: string) {
if (!trace) {
return;
}
process.stderr.write(`${trace}\n`);
static isLevelEnabled(level: LogLevel): boolean {
const logLevels = Logger.logLevels;
return isLogLevelEnabled(level, logLevels);
}
}

View File

@@ -0,0 +1 @@
export * from './is-log-level-enabled.util';

View File

@@ -0,0 +1,32 @@
import { LogLevel } from '../logger.service';
const LOG_LEVEL_VALUES: Record<LogLevel, number> = {
debug: 0,
verbose: 1,
log: 2,
warn: 3,
error: 4,
};
/**
* Checks if target level is enabled.
* @param targetLevel target level
* @param logLevels array of enabled log levels
*/
export function isLogLevelEnabled(
targetLevel: LogLevel,
logLevels: LogLevel[] | undefined,
): boolean {
if (!logLevels || (Array.isArray(logLevels) && logLevels?.length === 0)) {
return false;
}
if (logLevels.includes(targetLevel)) {
return true;
}
const highestLogLevelValue = logLevels
.map(level => LOG_LEVEL_VALUES[level])
.sort((a, b) => b - a)?.[0];
const targetLevelValue = LOG_LEVEL_VALUES[targetLevel];
return targetLevelValue >= highestLogLevelValue;
}

View File

@@ -16,7 +16,7 @@ describe('@Inject', () => {
const metadata = Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, Test);
const expectedMetadata = [
{ index: 2, param: opaqueToken.name },
{ index: 2, param: opaqueToken },
{ index: 1, param: 'test2' },
{ index: 0, param: 'test' },
];

View File

@@ -62,7 +62,7 @@ describe('HttpException', () => {
it('should be serializable', () => {
const message = 'Some Error';
const error = new HttpException(message, 400);
expect(`${error}`).to.be.eql(`Error: ${message}`);
expect(`${error}`).to.be.eql(`HttpException: ${message}`);
});
describe('when "response" is an object', () => {
@@ -71,8 +71,10 @@ describe('HttpException', () => {
const error = new HttpException(obj, 400);
const badRequestError = new BadRequestException(obj);
expect(`${error}`).to.be.eql(`Error: Http Exception`);
expect(`${badRequestError}`).to.be.eql(`Error: Bad Request Exception`);
expect(`${error}`).to.be.eql(`HttpException: Http Exception`);
expect(`${badRequestError}`).to.be.eql(
`BadRequestException: Bad Request Exception`,
);
expect(`${error}`.includes('[object Object]')).to.not.be.true;
expect(`${badRequestError}`.includes('[object Object]')).to.not.be.true;
});
@@ -80,7 +82,7 @@ describe('HttpException', () => {
it('should concat strings', () => {
const test = 'test message';
const error = new HttpException(test, 400);
expect(`${error}`).to.be.eql(`Error: ${test}`);
expect(`${error}`).to.be.eql(`HttpException: ${test}`);
expect(`${error}`.includes('[object Object]')).to.not.be.true;
});
});

View File

@@ -0,0 +1,502 @@
import { expect } from 'chai';
import 'reflect-metadata';
import * as sinon from 'sinon';
import { Logger, LoggerService } from '../../services';
describe('Logger', () => {
describe('[static methods]', () => {
describe('when the default logger is used', () => {
let processStdoutWriteSpy: sinon.SinonSpy;
let processStderrWriteSpy: sinon.SinonSpy;
beforeEach(() => {
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
processStderrWriteSpy = sinon.spy(process.stderr, 'write');
});
afterEach(() => {
processStdoutWriteSpy.restore();
processStderrWriteSpy.restore();
});
it('should print one message to the console', () => {
const message = 'random message';
const context = 'RandomContext';
Logger.log(message, context);
expect(processStdoutWriteSpy.calledOnce).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print one message without context to the console', () => {
const message = 'random message without context';
Logger.log(message);
expect(processStdoutWriteSpy.calledOnce).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print multiple messages to the console', () => {
const messages = ['message 1', 'message 2', 'message 3'];
const context = 'RandomContext';
Logger.log(messages[0], messages[1], messages[2], context);
expect(processStdoutWriteSpy.calledThrice).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
messages[0],
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
messages[1],
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
messages[2],
);
});
it('should print one error to the console', () => {
const message = 'random error';
const context = 'RandomContext';
Logger.error(message, context);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print one error without context to the console', () => {
const message = 'random error without context';
Logger.error(message);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print error object without context to the console', () => {
const error = new Error('Random text here');
Logger.error(error);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`Error: Random text here`,
);
});
it('should serialise a plain JS object (as a message) without context to the console', () => {
const error = {
randomError: true,
};
Logger.error(error);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(`Object:`);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`{\n "randomError": true\n}`,
);
});
it('should print one error with stacktrace and context to the console', () => {
const message = 'random error with context';
const stacktrace = 'stacktrace';
const context = 'ErrorContext';
Logger.error(message, stacktrace, context);
expect(processStderrWriteSpy.calledTwice).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
expect(processStderrWriteSpy.secondCall.firstArg).to.equal(
stacktrace + '\n',
);
});
it('should print multiple 2 errors and one stacktrace to the console', () => {
const messages = ['message 1', 'message 2'];
const stack = 'stacktrace';
const context = 'RandomContext';
Logger.error(messages[0], messages[1], stack, context);
expect(processStderrWriteSpy.calledThrice).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
messages[0],
);
expect(processStderrWriteSpy.secondCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.secondCall.firstArg).to.include(
messages[1],
);
expect(processStderrWriteSpy.thirdCall.firstArg).to.not.include(
`[${context}]`,
);
expect(processStderrWriteSpy.thirdCall.firstArg).to.equal(stack + '\n');
});
});
describe('when logging is disabled', () => {
let processStdoutWriteSpy: sinon.SinonSpy;
let previousLoggerRef: LoggerService;
beforeEach(() => {
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
previousLoggerRef =
Logger['localInstanceRef'] || Logger['staticInstanceRef'];
Logger.overrideLogger(false);
});
afterEach(() => {
processStdoutWriteSpy.restore();
Logger.overrideLogger(previousLoggerRef);
});
it('should not print any message to the console', () => {
const message = 'random message';
const context = 'RandomContext';
Logger.log(message, context);
expect(processStdoutWriteSpy.called).to.be.false;
});
});
describe('when custom logger is being used', () => {
class CustomLogger implements LoggerService {
log(message: any, context?: string) {}
error(message: any, trace?: string, context?: string) {}
warn(message: any, context?: string) {}
}
const customLogger = new CustomLogger();
let previousLoggerRef: LoggerService;
beforeEach(() => {
previousLoggerRef =
Logger['localInstanceRef'] || Logger['staticInstanceRef'];
Logger.overrideLogger(customLogger);
});
afterEach(() => {
Logger.overrideLogger(previousLoggerRef);
});
it('should call custom logger "#log()" method', () => {
const message = 'random message';
const context = 'RandomContext';
const customLoggerLogSpy = sinon.spy(customLogger, 'log');
Logger.log(message, context);
expect(customLoggerLogSpy.called).to.be.true;
expect(customLoggerLogSpy.calledWith(message, context)).to.be.true;
});
it('should call custom logger "#error()" method', () => {
const message = 'random message';
const context = 'RandomContext';
const customLoggerErrorSpy = sinon.spy(customLogger, 'error');
Logger.error(message, context);
expect(customLoggerErrorSpy.called).to.be.true;
expect(customLoggerErrorSpy.calledWith(message, context)).to.be.true;
});
});
});
describe('[instance methods]', () => {
describe('when the default logger is used', () => {
const logger = new Logger();
let processStdoutWriteSpy: sinon.SinonSpy;
let processStderrWriteSpy: sinon.SinonSpy;
beforeEach(() => {
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
processStderrWriteSpy = sinon.spy(process.stderr, 'write');
});
afterEach(() => {
processStdoutWriteSpy.restore();
processStderrWriteSpy.restore();
});
it('should print one message to the console', () => {
const message = 'random message';
const context = 'RandomContext';
logger.log(message, context);
expect(processStdoutWriteSpy.calledOnce).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print one message without context to the console', () => {
const message = 'random message without context';
logger.log(message);
expect(processStdoutWriteSpy.calledOnce).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print multiple messages to the console', () => {
const messages = ['message 1', 'message 2', 'message 3'];
const context = 'RandomContext';
logger.log(messages[0], messages[1], messages[2], context);
expect(processStdoutWriteSpy.calledThrice).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
messages[0],
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
messages[1],
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
`[${context}]`,
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
messages[2],
);
});
it('should print one error to the console', () => {
const message = 'random error';
const context = 'RandomContext';
logger.error(message, context);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print one error without context to the console', () => {
const message = 'random error without context';
logger.error(message);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
});
it('should print one error with stacktrace and context to the console', () => {
const message = 'random error with context';
const stacktrace = 'stacktrace';
const context = 'ErrorContext';
logger.error(message, stacktrace, context);
expect(processStderrWriteSpy.calledTwice).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(message);
expect(processStderrWriteSpy.secondCall.firstArg).to.equal(
stacktrace + '\n',
);
});
it('should print 2 errors and one stacktrace to the console', () => {
const messages = ['message 1', 'message 2'];
const stack = 'stacktrace';
const context = 'RandomContext';
logger.error(messages[0], messages[1], stack, context);
expect(processStderrWriteSpy.calledThrice).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
messages[0],
);
expect(processStderrWriteSpy.secondCall.firstArg).to.include(
`[${context}]`,
);
expect(processStderrWriteSpy.secondCall.firstArg).to.include(
messages[1],
);
expect(processStderrWriteSpy.thirdCall.firstArg).to.not.include(
`[${context}]`,
);
expect(processStderrWriteSpy.thirdCall.firstArg).to.equal(stack + '\n');
});
});
describe('when the default logger is used and global context is set and timestamp enabled', () => {
const globalContext = 'GlobalContext';
const logger = new Logger(globalContext, { timestamp: true });
let processStdoutWriteSpy: sinon.SinonSpy;
let processStderrWriteSpy: sinon.SinonSpy;
beforeEach(() => {
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
processStderrWriteSpy = sinon.spy(process.stderr, 'write');
});
afterEach(() => {
processStdoutWriteSpy.restore();
processStderrWriteSpy.restore();
});
it('should print multiple messages to the console and append global context', () => {
const messages = ['message 1', 'message 2', 'message 3'];
logger.log(messages[0], messages[1], messages[2]);
expect(processStdoutWriteSpy.calledThrice).to.be.true;
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
`[${globalContext}]`,
);
expect(processStdoutWriteSpy.firstCall.firstArg).to.include(
messages[0],
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
`[${globalContext}]`,
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include(
messages[1],
);
expect(processStdoutWriteSpy.secondCall.firstArg).to.include('ms');
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
`[${globalContext}]`,
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include(
messages[2],
);
expect(processStdoutWriteSpy.thirdCall.firstArg).to.include('ms');
});
});
describe('when logging is disabled', () => {
const logger = new Logger();
let processStdoutWriteSpy: sinon.SinonSpy;
let previousLoggerRef: LoggerService;
beforeEach(() => {
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
previousLoggerRef =
Logger['localInstanceRef'] || Logger['staticInstanceRef'];
Logger.overrideLogger(false);
});
afterEach(() => {
processStdoutWriteSpy.restore();
Logger.overrideLogger(previousLoggerRef);
});
it('should not print any message to the console', () => {
const message = 'random message';
const context = 'RandomContext';
logger.log(message, context);
expect(processStdoutWriteSpy.called).to.be.false;
});
});
describe('when custom logger is being used', () => {
class CustomLogger implements LoggerService {
log(message: any, context?: string) {}
error(message: any, trace?: string, context?: string) {}
warn(message: any, context?: string) {}
}
const customLogger = new CustomLogger();
const originalLogger = new Logger();
let previousLoggerRef: LoggerService;
beforeEach(() => {
previousLoggerRef =
Logger['localInstanceRef'] || Logger['staticInstanceRef'];
Logger.overrideLogger(customLogger);
});
afterEach(() => {
Logger.overrideLogger(previousLoggerRef);
});
it('should call custom logger "#log()" method', () => {
const message = 'random message';
const context = 'RandomContext';
const customLoggerLogSpy = sinon.spy(customLogger, 'log');
originalLogger.log(message, context);
expect(customLoggerLogSpy.called).to.be.true;
expect(customLoggerLogSpy.calledWith(message, context)).to.be.true;
});
it('should call custom logger "#error()" method', () => {
const message = 'random message';
const context = 'RandomContext';
const customLoggerErrorSpy = sinon.spy(customLogger, 'error');
originalLogger.error(message, context);
expect(customLoggerErrorSpy.called).to.be.true;
expect(customLoggerErrorSpy.calledWith(message, context)).to.be.true;
});
});
});
});

View File

@@ -0,0 +1,41 @@
import { expect } from 'chai';
import { LogLevel } from '../../../services/logger.service';
import { isLogLevelEnabled } from '../../../services/utils';
describe('isLogLevelEnabled', () => {
const tests = [
{ inputArgs: ['log', ['log']], expectedReturnValue: true },
{ inputArgs: ['debug', ['debug']], expectedReturnValue: true },
{ inputArgs: ['verbose', ['verbose']], expectedReturnValue: true },
{ inputArgs: ['error', ['error']], expectedReturnValue: true },
{ inputArgs: ['warn', ['warn']], expectedReturnValue: true },
/** explicitly included + log level is higher than target */
{ inputArgs: ['log', ['error', 'log']], expectedReturnValue: true },
{ inputArgs: ['warn', ['warn', 'error']], expectedReturnValue: true },
{ inputArgs: ['debug', ['warn', 'debug']], expectedReturnValue: true },
{ inputArgs: ['verbose', ['error', 'verbose']], expectedReturnValue: true },
/** not explicitly included + log level is higher than target */
{ inputArgs: ['log', ['error', 'warn']], expectedReturnValue: false },
{ inputArgs: ['verbose', ['warn']], expectedReturnValue: false },
{ inputArgs: ['debug', ['warn', 'error']], expectedReturnValue: false },
{ inputArgs: ['warn', ['error']], expectedReturnValue: false },
];
for (const { inputArgs, expectedReturnValue } of tests) {
describe(`when log levels = [${inputArgs[1]}]`, () => {
describe(`and target level is "${inputArgs[0]}"`, () => {
it('should return true', () => {
expect(
isLogLevelEnabled(...(inputArgs as [LogLevel, LogLevel[]])),
).to.equal(expectedReturnValue);
});
});
});
}
describe(`when log levels = undefined`, () => {
it('should return false', () => {
expect(isLogLevelEnabled('warn', undefined)).to.be.false;
});
});
});

View File

@@ -9,6 +9,7 @@ import {
isPlainObject,
isString,
isUndefined,
normalizePath,
} from '../../utils/shared.utils';
function Foo(a) {
@@ -17,34 +18,34 @@ function Foo(a) {
describe('Shared utils', () => {
describe('isUndefined', () => {
it('should returns true when obj is undefined', () => {
it('should return true when obj is undefined', () => {
expect(isUndefined(undefined)).to.be.true;
});
it('should returns false when object is not undefined', () => {
it('should return false when object is not undefined', () => {
expect(isUndefined({})).to.be.false;
});
});
describe('isFunction', () => {
it('should returns true when obj is function', () => {
it('should return true when obj is function', () => {
expect(isFunction(() => ({}))).to.be.true;
});
it('should returns false when object is not function', () => {
it('should return false when object is not function', () => {
expect(isFunction(null)).to.be.false;
expect(isFunction(undefined)).to.be.false;
});
});
describe('isObject', () => {
it('should returns true when obj is object', () => {
it('should return true when obj is object', () => {
expect(isObject({})).to.be.true;
});
it('should returns false when object is not object', () => {
it('should return false when object is not object', () => {
expect(isObject(3)).to.be.false;
expect(isObject(null)).to.be.false;
expect(isObject(undefined)).to.be.false;
});
});
describe('isPlainObject', () => {
it('should returns true when obj is plain object', () => {
it('should return true when obj is plain object', () => {
expect(isPlainObject({})).to.be.true;
expect(isPlainObject({ prop: true })).to.be.true;
expect(
@@ -54,7 +55,7 @@ describe('Shared utils', () => {
).to.be.true;
expect(isPlainObject(Object.create(null))).to.be.true;
});
it('should returns false when object is not object', () => {
it('should return false when object is not object', () => {
expect(isPlainObject(3)).to.be.false;
expect(isPlainObject(null)).to.be.false;
expect(isPlainObject(undefined)).to.be.false;
@@ -64,52 +65,69 @@ describe('Shared utils', () => {
});
});
describe('isString', () => {
it('should returns true when obj is string', () => {
it('should return true when obj is a string', () => {
expect(isString('true')).to.be.true;
});
it('should returns false when object is not string', () => {
it('should return false when object is not a string', () => {
expect(isString(false)).to.be.false;
expect(isString(null)).to.be.false;
expect(isString(undefined)).to.be.false;
});
});
describe('isConstructor', () => {
it('should returns true when string is equal constructor', () => {
it('should return true when string is equal to constructor', () => {
expect(isConstructor('constructor')).to.be.true;
});
it('should returns false when string is not equal constructor', () => {
it('should return false when string is not equal to constructor', () => {
expect(isConstructor('nope')).to.be.false;
});
});
describe('addLeadingSlash', () => {
it('should returns validated path ("add / if not exists")', () => {
it('should return the validated path ("add / if not exists")', () => {
expect(addLeadingSlash('nope')).to.be.eql('/nope');
});
it('should returns same path', () => {
it('should return the same path', () => {
expect(addLeadingSlash('/nope')).to.be.eql('/nope');
});
it('should returns empty path', () => {
it('should return empty path', () => {
expect(addLeadingSlash('')).to.be.eql('');
expect(addLeadingSlash(null)).to.be.eql('');
expect(addLeadingSlash(undefined)).to.be.eql('');
});
});
describe('normalizePath', () => {
it('should remove all trailing slashes at the end of the path', () => {
expect(normalizePath('path/')).to.be.eql('/path');
expect(normalizePath('path///')).to.be.eql('/path');
expect(normalizePath('/path/path///')).to.be.eql('/path/path');
});
it('should replace all slashes with only one slash', () => {
expect(normalizePath('////path/')).to.be.eql('/path');
expect(normalizePath('///')).to.be.eql('/');
expect(normalizePath('/path////path///')).to.be.eql('/path/path');
});
it('should return / for empty path', () => {
expect(normalizePath('')).to.be.eql('/');
expect(normalizePath(null)).to.be.eql('/');
expect(normalizePath(undefined)).to.be.eql('/');
});
});
describe('isNil', () => {
it('should returns true when obj is undefined or null', () => {
it('should return true when obj is undefined or null', () => {
expect(isNil(undefined)).to.be.true;
expect(isNil(null)).to.be.true;
});
it('should returns false when object is not undefined and null', () => {
it('should return false when object is not undefined and null', () => {
expect(isNil('3')).to.be.false;
});
});
describe('isEmpty', () => {
it('should returns true when array is empty or not exists', () => {
it('should return true when array is empty or not exists', () => {
expect(isEmpty([])).to.be.true;
expect(isEmpty(null)).to.be.true;
expect(isEmpty(undefined)).to.be.true;
});
it('should returns false when array is not empty', () => {
it('should return false when array is not empty', () => {
expect(isEmpty([1, 2])).to.be.false;
});
});

View File

@@ -33,6 +33,13 @@ export const addLeadingSlash = (path?: string): string =>
*/
export const validatePath = addLeadingSlash;
export const normalizePath = (path?: string): string =>
path
? path.startsWith('/')
? ('/' + path.replace(/\/+$/, '')).replace(/\/+/g, '/')
: '/' + path.replace(/\/+$/, '')
: '/';
export const isFunction = (fn: any): boolean => typeof fn === 'function';
export const isString = (fn: any): fn is string => typeof fn === 'string';
export const isConstructor = (fn: any): boolean => fn === 'constructor';

View File

@@ -17,14 +17,6 @@ export abstract class AbstractHttpAdapter<
protected httpServer: TServer;
constructor(protected readonly instance: any) {}
all(path: string, handler: RequestHandler<TRequest, TResponse>);
all(handler: RequestHandler<TRequest, TResponse>);
all(path: any, handler?: any) {
throw new Error('Method not implemented.');
}
setBaseViewsDir?(path: string | string[]): this {
throw new Error('Method not implemented.');
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public async init() {}
@@ -69,6 +61,12 @@ export abstract class AbstractHttpAdapter<
return this.instance.patch(...args);
}
public all(handler: RequestHandler);
public all(path: any, handler: RequestHandler);
public all(...args: any[]) {
return this.instance.all(...args);
}
public options(handler: RequestHandler);
public options(path: any, handler: RequestHandler);
public options(...args: any[]) {

View File

@@ -1,3 +1,4 @@
import { Logger } from '@nestjs/common';
import { ExceptionHandler } from './exception-handler';
const DEFAULT_TEARDOWN = () => process.exit(1);
@@ -8,11 +9,15 @@ export class ExceptionsZone {
public static run(
callback: () => void,
teardown: (err: any) => void = DEFAULT_TEARDOWN,
autoFlushLogs?: boolean,
) {
try {
callback();
} catch (e) {
this.exceptionHandler.handle(e);
if (autoFlushLogs) {
Logger.flush();
}
teardown(e);
}
}
@@ -20,11 +25,15 @@ export class ExceptionsZone {
public static async asyncRun(
callback: () => Promise<void>,
teardown: (err: any) => void = DEFAULT_TEARDOWN,
autoFlushLogs?: boolean,
) {
try {
await callback();
} catch (e) {
this.exceptionHandler.handle(e);
if (autoFlushLogs) {
Logger.flush();
}
teardown(e);
}
}

View File

@@ -46,10 +46,15 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
host: ArgumentsHost,
applicationRef: AbstractHttpAdapter | HttpServer,
) {
const body = {
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
};
const body = this.isHttpError(exception)
? {
statusCode: exception.statusCode,
message: exception.message,
}
: {
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
};
applicationRef.reply(host.getArgByIndex(1), body, body.statusCode);
if (this.isExceptionObject(exception)) {
return BaseExceptionFilter.logger.error(
@@ -63,4 +68,12 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
public isExceptionObject(err: any): err is Error {
return isObject(err) && !!(err as Error).message;
}
/**
* Checks if the thrown error comes from the "http-errors" library.
* @param err error object
*/
public isHttpError(err: any): err is { statusCode: number; message: string } {
return err?.statusCode && err?.message;
}
}

View File

@@ -1,6 +1,6 @@
import { CanActivate } from '@nestjs/common';
import { GUARDS_METADATA } from '@nestjs/common/constants';
import { Controller } from '@nestjs/common/interfaces';
import { Controller, Type } from '@nestjs/common/interfaces';
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { ApplicationConfig } from '../application-config';
@@ -54,15 +54,17 @@ export class GuardsContextCreator extends ContextCreator {
}
public getGuardInstance(
guard: Function | CanActivate,
metatype: Function | CanActivate,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): CanActivate | null {
const isObject = (guard as CanActivate).canActivate;
const isObject = (metatype as CanActivate).canActivate;
if (isObject) {
return guard as CanActivate;
return metatype as CanActivate;
}
const instanceWrapper = this.getInstanceByMetatype(guard);
const instanceWrapper = this.getInstanceByMetatype(
metatype as Type<unknown>,
);
if (!instanceWrapper) {
return null;
}
@@ -73,8 +75,8 @@ export class GuardsContextCreator extends ContextCreator {
return instanceHost && instanceHost.instance;
}
public getInstanceByMetatype<T extends Record<string, any> | Function>(
guard: T,
public getInstanceByMetatype(
metatype: Type<unknown>,
): InstanceWrapper | undefined {
if (!this.moduleContext) {
return;
@@ -85,7 +87,7 @@ export class GuardsContextCreator extends ContextCreator {
return;
}
const injectables = moduleRef.injectables;
return injectables.get(guard.name as string);
return injectables.get(metatype);
}
public getGlobalMetadata<T extends unknown[]>(

View File

@@ -13,7 +13,6 @@ import { GuardsContextCreator } from '../guards/guards-context-creator';
import { STATIC_CONTEXT } from '../injector/constants';
import { NestContainer } from '../injector/container';
import { ContextId } from '../injector/instance-wrapper';
import { Module } from '../injector/module';
import { ModulesContainer } from '../injector/modules-container';
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
@@ -105,7 +104,7 @@ export class ExternalContextCreator {
},
contextType: TContext = 'http' as TContext,
) {
const module = this.getContextModuleName(instance.constructor);
const module = this.getContextModuleKey(instance.constructor);
const { argsLength, paramtypes, getParamsMetadata } = this.getMetadata<
TParamsMetadata,
TContext
@@ -238,26 +237,18 @@ export class ExternalContextCreator {
return handlerMetadata;
}
public getContextModuleName(constructor: Function): string {
const defaultModuleName = '';
const className = constructor.name;
if (!className) {
return defaultModuleName;
public getContextModuleKey(moduleCtor: Function | undefined): string {
const emptyModuleKey = '';
if (!moduleCtor) {
return emptyModuleKey;
}
for (const [key, module] of [...this.modulesContainer.entries()]) {
if (this.getProviderByClassName(module, className)) {
const moduleContainerEntries = this.modulesContainer.entries();
for (const [key, moduleRef] of moduleContainerEntries) {
if (moduleRef.hasProvider(moduleCtor)) {
return key;
}
}
return defaultModuleName;
}
public getProviderByClassName(module: Module, className: string): boolean {
const { providers } = module;
const hasProvider = [...providers.keys()].some(
provider => provider === className,
);
return hasProvider;
return emptyModuleKey;
}
public exchangeKeysForValues<TMetadata = any>(

View File

@@ -7,7 +7,7 @@ export class RouterMethodFactory {
case RequestMethod.POST:
return target.post;
case RequestMethod.ALL:
return target.use;
return target.all;
case RequestMethod.DELETE:
return target.delete;
case RequestMethod.PUT:

View File

@@ -56,7 +56,7 @@ export class NestContainer {
scope: Type<any>[],
): Promise<Module> {
// In DependenciesScanner#scanForModules we already check for undefined or invalid modules
// We sill need to catch the edge-case of `forwardRef(() => undefined)`
// We still need to catch the edge-case of `forwardRef(() => undefined)`
if (!metatype) {
throw new UndefinedForwardRefException(scope);
}
@@ -143,7 +143,10 @@ export class NestContainer {
moduleRef.addRelatedModule(related);
}
public addProvider(provider: Provider, token: string): string {
public addProvider(
provider: Provider,
token: string,
): string | symbol | Function {
if (!provider) {
throw new CircularDependencyException();
}

View File

@@ -1,12 +1,13 @@
import { iterate } from 'iterare';
import { InstanceWrapper } from '../instance-wrapper';
import { InstanceToken } from '../module';
/**
* Returns the instances which are transient
* @param instances The instances which should be checked whether they are transcient
*/
export function getTransientInstances(
instances: [string, InstanceWrapper][],
instances: [InstanceToken, InstanceWrapper][],
): InstanceWrapper[] {
return iterate(instances)
.filter(([_, wrapper]) => wrapper.isDependencyTreeStatic())
@@ -22,7 +23,7 @@ export function getTransientInstances(
* @param instances The instances which should be checked whether they are transcient
*/
export function getNonTransientInstances(
instances: [string, InstanceWrapper][],
instances: [InstanceToken, InstanceWrapper][],
): InstanceWrapper[] {
return iterate(instances)
.filter(

View File

@@ -9,7 +9,6 @@ import { Controller } from '@nestjs/common/interfaces/controllers/controller.int
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
import { Type } from '@nestjs/common/interfaces/type.interface';
import {
isFunction,
isNil,
isObject,
isString,
@@ -27,7 +26,7 @@ import {
InstanceWrapper,
PropertyMetadata,
} from './instance-wrapper';
import { Module } from './module';
import { InstanceToken, Module } from './module';
/**
* The type of an injectable dependency
@@ -54,9 +53,9 @@ export interface InjectorDependencyContext {
*/
key?: string | symbol;
/**
* The name of the function or injection token
* The function itself, the name of the function, or injection token.
*/
name?: string | symbol;
name?: Function | string | symbol;
/**
* The index of the dependency which gets injected
* from the dependencies array
@@ -70,27 +69,27 @@ export interface InjectorDependencyContext {
export class Injector {
public loadPrototype<T>(
{ name }: InstanceWrapper<T>,
collection: Map<string, InstanceWrapper<T>>,
{ token }: InstanceWrapper<T>,
collection: Map<InstanceToken, InstanceWrapper<T>>,
contextId = STATIC_CONTEXT,
) {
if (!collection) {
return;
}
const target = collection.get(name);
const target = collection.get(token);
const instance = target.createPrototype(contextId);
if (instance) {
const wrapper = new InstanceWrapper({
...target,
instance,
});
collection.set(name, wrapper);
collection.set(token, wrapper);
}
}
public async loadInstance<T>(
wrapper: InstanceWrapper<T>,
collection: Map<string, InstanceWrapper>,
collection: Map<InstanceToken, InstanceWrapper>,
moduleRef: Module,
contextId = STATIC_CONTEXT,
inquirer?: InstanceWrapper,
@@ -101,9 +100,10 @@ export class Injector {
return instanceHost.donePromise;
}
const done = this.applyDoneHook(instanceHost);
const { name, inject } = wrapper;
const token = wrapper.token || wrapper.name;
const targetWrapper = collection.get(name);
const { inject } = wrapper;
const targetWrapper = collection.get(token);
if (isUndefined(targetWrapper)) {
throw new RuntimeException();
}
@@ -142,13 +142,13 @@ export class Injector {
public async loadMiddleware(
wrapper: InstanceWrapper,
collection: Map<string, InstanceWrapper>,
collection: Map<InstanceToken, InstanceWrapper>,
moduleRef: Module,
contextId = STATIC_CONTEXT,
inquirer?: InstanceWrapper,
) {
const { metatype } = wrapper;
const targetWrapper = collection.get(metatype.name);
const { metatype, token } = wrapper;
const targetWrapper = collection.get(token);
if (!isUndefined(targetWrapper.instance)) {
return;
}
@@ -317,7 +317,7 @@ export class Injector {
const token = this.resolveParamToken(wrapper, param);
return this.resolveComponentInstance<T>(
moduleRef,
isFunction(token) ? (token as Type<any>).name : token,
token,
dependencyContext,
wrapper,
contextId,
@@ -339,7 +339,7 @@ export class Injector {
public async resolveComponentInstance<T>(
moduleRef: Module,
name: any,
token: InstanceToken,
dependencyContext: InjectorDependencyContext,
wrapper: InstanceWrapper<T>,
contextId = STATIC_CONTEXT,
@@ -350,7 +350,7 @@ export class Injector {
const instanceWrapper = await this.lookupComponent(
providers,
moduleRef,
{ ...dependencyContext, name },
{ ...dependencyContext, name: token },
wrapper,
contextId,
inquirer,
@@ -407,7 +407,7 @@ export class Injector {
}
public async lookupComponent<T = any>(
providers: Map<string | symbol, InstanceWrapper>,
providers: Map<Function | string | symbol, InstanceWrapper>,
moduleRef: Module,
dependencyContext: InjectorDependencyContext,
wrapper: InstanceWrapper<T>,
@@ -467,7 +467,7 @@ export class Injector {
public async lookupComponentInImports(
moduleRef: Module,
name: any,
name: InstanceToken,
wrapper: InstanceWrapper,
moduleRegistry: any[] = [],
contextId = STATIC_CONTEXT,
@@ -484,7 +484,7 @@ export class Injector {
if (isTraversing) {
const contextModuleExports = moduleRef.exports;
children = children.filter(child =>
contextModuleExports.has(child.metatype && child.metatype.name),
contextModuleExports.has(child.metatype),
);
}
for (const relatedModule of children) {
@@ -552,7 +552,7 @@ export class Injector {
try {
const dependencyContext = {
key: item.key,
name: item.name as string,
name: item.name as Function | string | symbol,
};
if (this.isInquirer(item.name, parentInquirer)) {
return parentInquirer && parentInquirer.instance;
@@ -656,15 +656,12 @@ export class Injector {
public async loadPerContext<T = any>(
instance: T,
moduleRef: Module,
collection: Map<string, InstanceWrapper>,
collection: Map<InstanceToken, InstanceWrapper>,
ctx: ContextId,
wrapper?: InstanceWrapper,
): Promise<T> {
if (!wrapper) {
const providerCtor = instance.constructor;
const injectionToken =
(providerCtor && providerCtor.name) ||
((providerCtor as unknown) as string);
const injectionToken = instance.constructor;
wrapper = collection.get(injectionToken);
}
await this.loadInstance(wrapper, collection, moduleRef, ctx, wrapper);

View File

@@ -1,11 +1,9 @@
import { Abstract, Type } from '@nestjs/common';
import { isFunction } from '@nestjs/common/utils/shared.utils';
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
import { NestContainer } from './container';
import { InstanceWrapper } from './instance-wrapper';
import { Module } from './module';
import { InstanceToken, Module } from './module';
type InstanceToken = string | symbol | Type<any> | Abstract<any> | Function;
type HostCollection = 'providers' | 'controllers' | 'injectables';
export interface InstanceLink<T = any> {
@@ -23,20 +21,17 @@ export class InstanceLinksHost {
}
get<T = any>(token: InstanceToken, moduleId?: string): InstanceLink<T> {
const name = isFunction(token)
? (token as Function).name
: (token as string | symbol);
const modulesMap = this.instanceLinks.get(name);
const modulesMap = this.instanceLinks.get(token);
if (!modulesMap) {
throw new UnknownElementException(name);
throw new UnknownElementException(this.getInstanceNameByToken(token));
}
const instanceLink = moduleId
? modulesMap.find(item => item.moduleId === moduleId)
: modulesMap[modulesMap.length - 1];
if (!instanceLink) {
throw new UnknownElementException(name);
throw new UnknownElementException(this.getInstanceNameByToken(token));
}
return instanceLink;
}
@@ -76,4 +71,8 @@ export class InstanceLinksHost {
existingLinks.push(instanceLink);
}
}
private getInstanceNameByToken(token: InstanceToken): string {
return isFunction(token) ? (token as Function)?.name : (token as string);
}
}

View File

@@ -9,7 +9,9 @@ import { Module } from './module';
export class InstanceLoader {
private readonly injector = new Injector();
private readonly logger = new Logger(InstanceLoader.name, true);
private readonly logger = new Logger(InstanceLoader.name, {
timestamp: true,
});
constructor(private readonly container: NestContainer) {}
@@ -21,69 +23,69 @@ export class InstanceLoader {
}
private createPrototypes(modules: Map<string, Module>) {
modules.forEach(module => {
this.createPrototypesOfProviders(module);
this.createPrototypesOfInjectables(module);
this.createPrototypesOfControllers(module);
modules.forEach(moduleRef => {
this.createPrototypesOfProviders(moduleRef);
this.createPrototypesOfInjectables(moduleRef);
this.createPrototypesOfControllers(moduleRef);
});
}
private async createInstances(modules: Map<string, Module>) {
await Promise.all(
[...modules.values()].map(async module => {
await this.createInstancesOfProviders(module);
await this.createInstancesOfInjectables(module);
await this.createInstancesOfControllers(module);
[...modules.values()].map(async moduleRef => {
await this.createInstancesOfProviders(moduleRef);
await this.createInstancesOfInjectables(moduleRef);
await this.createInstancesOfControllers(moduleRef);
const { name } = module.metatype;
const { name } = moduleRef.metatype;
this.isModuleWhitelisted(name) &&
this.logger.log(MODULE_INIT_MESSAGE`${name}`);
}),
);
}
private createPrototypesOfProviders(module: Module) {
const { providers } = module;
private createPrototypesOfProviders(moduleRef: Module) {
const { providers } = moduleRef;
providers.forEach(wrapper =>
this.injector.loadPrototype<Injectable>(wrapper, providers),
);
}
private async createInstancesOfProviders(module: Module) {
const { providers } = module;
private async createInstancesOfProviders(moduleRef: Module) {
const { providers } = moduleRef;
const wrappers = [...providers.values()];
await Promise.all(
wrappers.map(item => this.injector.loadProvider(item, module)),
wrappers.map(item => this.injector.loadProvider(item, moduleRef)),
);
}
private createPrototypesOfControllers(module: Module) {
const { controllers } = module;
private createPrototypesOfControllers(moduleRef: Module) {
const { controllers } = moduleRef;
controllers.forEach(wrapper =>
this.injector.loadPrototype<Controller>(wrapper, controllers),
);
}
private async createInstancesOfControllers(module: Module) {
const { controllers } = module;
private async createInstancesOfControllers(moduleRef: Module) {
const { controllers } = moduleRef;
const wrappers = [...controllers.values()];
await Promise.all(
wrappers.map(item => this.injector.loadController(item, module)),
wrappers.map(item => this.injector.loadController(item, moduleRef)),
);
}
private createPrototypesOfInjectables(module: Module) {
const { injectables } = module;
private createPrototypesOfInjectables(moduleRef: Module) {
const { injectables } = moduleRef;
injectables.forEach(wrapper =>
this.injector.loadPrototype(wrapper, injectables),
);
}
private async createInstancesOfInjectables(module: Module) {
const { injectables } = module;
private async createInstancesOfInjectables(moduleRef: Module) {
const { injectables } = moduleRef;
const wrappers = [...injectables.values()];
await Promise.all(
wrappers.map(item => this.injector.loadInjectable(item, module)),
wrappers.map(item => this.injector.loadInjectable(item, moduleRef)),
);
}

View File

@@ -8,7 +8,7 @@ import { randomStringGenerator } from '@nestjs/common/utils/random-string-genera
import { isNil, isUndefined } from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { STATIC_CONTEXT } from './constants';
import { Module } from './module';
import { InstanceToken, Module } from './module';
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
@@ -36,6 +36,7 @@ interface InstanceMetadataStore {
export class InstanceWrapper<T = any> {
public readonly name: any;
public readonly token: InstanceToken;
public readonly async?: boolean;
public readonly host?: Module;
public readonly isAlias: boolean = false;

View File

@@ -4,10 +4,25 @@ import { requestProvider } from '../router/request/request-providers';
import { Reflector } from '../services';
import { inquirerProvider } from './inquirer/inquirer-providers';
const ReflectorAliasProvider = {
provide: Reflector.name,
useExisting: Reflector,
};
@Global()
@Module({
providers: [Reflector, requestProvider, inquirerProvider],
exports: [Reflector, requestProvider, inquirerProvider],
providers: [
Reflector,
ReflectorAliasProvider,
requestProvider,
inquirerProvider,
],
exports: [
Reflector,
ReflectorAliasProvider,
requestProvider,
inquirerProvider,
],
})
export class InternalCoreModule {
static register(providers: ValueProvider[]): DynamicModule {

View File

@@ -31,21 +31,33 @@ import { NestContainer } from './container';
import { InstanceWrapper } from './instance-wrapper';
import { ModuleRef } from './module-ref';
interface ProviderName {
name?: string | symbol;
}
export type InstanceToken =
| string
| symbol
| Type<any>
| Abstract<any>
| Function;
export class Module {
private readonly _id: string;
private readonly _imports = new Set<Module>();
private readonly _providers = new Map<any, InstanceWrapper<Injectable>>();
private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>();
private readonly _middlewares = new Map<any, InstanceWrapper<Injectable>>();
private readonly _providers = new Map<
InstanceToken,
InstanceWrapper<Injectable>
>();
private readonly _injectables = new Map<
InstanceToken,
InstanceWrapper<Injectable>
>();
private readonly _middlewares = new Map<
InstanceToken,
InstanceWrapper<Injectable>
>();
private readonly _controllers = new Map<
string,
InstanceToken,
InstanceWrapper<Controller>
>();
private readonly _exports = new Set<string | symbol>();
private readonly _exports = new Set<InstanceToken>();
private _distance = 0;
constructor(
@@ -60,11 +72,11 @@ export class Module {
return this._id;
}
get providers(): Map<any, InstanceWrapper<Injectable>> {
get providers(): Map<InstanceToken, InstanceWrapper<Injectable>> {
return this._providers;
}
get middlewares(): Map<any, InstanceWrapper<Injectable>> {
get middlewares(): Map<InstanceToken, InstanceWrapper<Injectable>> {
return this._middlewares;
}
@@ -82,34 +94,34 @@ export class Module {
/**
* Left for backward-compatibility reasons
*/
get components(): Map<string, InstanceWrapper<Injectable>> {
get components(): Map<InstanceToken, InstanceWrapper<Injectable>> {
return this._providers;
}
/**
* Left for backward-compatibility reasons
*/
get routes(): Map<string, InstanceWrapper<Controller>> {
get routes(): Map<InstanceToken, InstanceWrapper<Controller>> {
return this._controllers;
}
get injectables(): Map<string, InstanceWrapper<Injectable>> {
get injectables(): Map<InstanceToken, InstanceWrapper<Injectable>> {
return this._injectables;
}
get controllers(): Map<string, InstanceWrapper<Controller>> {
get controllers(): Map<InstanceToken, InstanceWrapper<Controller>> {
return this._controllers;
}
get exports(): Set<string | symbol> {
get exports(): Set<InstanceToken> {
return this._exports;
}
get instance(): NestModule {
if (!this._providers.has(this._metatype.name)) {
if (!this._providers.has(this._metatype)) {
throw new RuntimeException();
}
const module = this._providers.get(this._metatype.name);
const module = this._providers.get(this._metatype);
return module.instance as NestModule;
}
@@ -134,8 +146,9 @@ export class Module {
public addModuleRef() {
const moduleRef = this.createModuleReferenceType();
this._providers.set(
ModuleRef.name,
ModuleRef,
new InstanceWrapper({
token: ModuleRef,
name: ModuleRef.name,
metatype: ModuleRef as any,
isResolved: true,
@@ -147,8 +160,9 @@ export class Module {
public addModuleAsProvider() {
this._providers.set(
this._metatype.name,
this._metatype,
new InstanceWrapper({
token: this._metatype,
name: this._metatype.name,
metatype: this._metatype,
isResolved: false,
@@ -160,8 +174,9 @@ export class Module {
public addApplicationConfig() {
this._providers.set(
ApplicationConfig.name,
ApplicationConfig,
new InstanceWrapper({
token: ApplicationConfig,
name: ApplicationConfig.name,
isResolved: true,
instance: this.container.applicationConfig,
@@ -177,9 +192,10 @@ export class Module {
if (this.isCustomProvider(injectable)) {
return this.addCustomProvider(injectable, this._injectables);
}
let instanceWrapper = this.injectables.get(injectable.name);
let instanceWrapper = this.injectables.get(injectable);
if (!instanceWrapper) {
instanceWrapper = new InstanceWrapper({
token: injectable,
name: injectable.name,
metatype: injectable,
instance: null,
@@ -187,23 +203,23 @@ export class Module {
scope: getClassScope(injectable),
host: this,
});
this._injectables.set(injectable.name, instanceWrapper);
this._injectables.set(injectable, instanceWrapper);
}
if (host) {
const token = host && host.name;
const hostWrapper =
this._controllers.get(host && host.name) || this._providers.get(token);
this._controllers.get(host) || this._providers.get(host);
hostWrapper && hostWrapper.addEnhancerMetadata(instanceWrapper);
}
}
public addProvider(provider: Provider): string {
public addProvider(provider: Provider) {
if (this.isCustomProvider(provider)) {
return this.addCustomProvider(provider, this._providers);
}
this._providers.set(
(provider as Type<Injectable>).name,
provider,
new InstanceWrapper({
token: provider,
name: (provider as Type<Injectable>).name,
metatype: provider as Type<Injectable>,
instance: null,
@@ -212,7 +228,7 @@ export class Module {
host: this,
}),
);
return (provider as Type<Injectable>).name;
return provider as Type<Injectable>;
}
public isCustomProvider(
@@ -232,20 +248,13 @@ export class Module {
}
public addCustomProvider(
provider: (
provider:
| ClassProvider
| FactoryProvider
| ValueProvider
| ExistingProvider
) &
ProviderName,
collection: Map<string, any>,
): string {
const name = this.getProviderStaticToken(provider.provide) as string;
provider = {
...provider,
name,
};
| ExistingProvider,
collection: Map<Function | string | symbol, any>,
) {
if (this.isCustomClass(provider)) {
this.addCustomClass(provider, collection);
} else if (this.isCustomValue(provider)) {
@@ -255,7 +264,7 @@ export class Module {
} else if (this.isCustomUseExisting(provider)) {
this.addCustomUseExisting(provider, collection);
}
return name;
return provider.provide;
}
public isCustomClass(provider: any): provider is ClassProvider {
@@ -279,19 +288,20 @@ export class Module {
}
public addCustomClass(
provider: ClassProvider & ProviderName,
collection: Map<string, InstanceWrapper>,
provider: ClassProvider,
collection: Map<InstanceToken, InstanceWrapper>,
) {
const { name, useClass } = provider;
let { scope } = provider;
const { useClass } = provider;
if (isUndefined(scope)) {
scope = getClassScope(useClass);
}
collection.set(
name as string,
provider.provide,
new InstanceWrapper({
name,
token: provider.provide,
name: useClass?.name || useClass,
metatype: useClass,
instance: null,
isResolved: false,
@@ -302,14 +312,15 @@ export class Module {
}
public addCustomValue(
provider: ValueProvider & ProviderName,
collection: Map<string, InstanceWrapper>,
provider: ValueProvider,
collection: Map<Function | string | symbol, InstanceWrapper>,
) {
const { name, useValue: value } = provider;
const { useValue: value, provide: providerToken } = provider;
collection.set(
name as string,
providerToken,
new InstanceWrapper({
name,
token: providerToken,
name: (providerToken as Function)?.name || providerToken,
metatype: null,
instance: value,
isResolved: true,
@@ -320,14 +331,21 @@ export class Module {
}
public addCustomFactory(
provider: FactoryProvider & ProviderName,
collection: Map<string, InstanceWrapper>,
provider: FactoryProvider,
collection: Map<Function | string | symbol, InstanceWrapper>,
) {
const { name, useFactory: factory, inject, scope } = provider;
const {
useFactory: factory,
inject,
scope,
provide: providerToken,
} = provider;
collection.set(
name as string,
providerToken,
new InstanceWrapper({
name,
token: providerToken,
name: (providerToken as Function)?.name || providerToken,
metatype: factory as any,
instance: null,
isResolved: false,
@@ -339,14 +357,15 @@ export class Module {
}
public addCustomUseExisting(
provider: ExistingProvider & ProviderName,
collection: Map<string, InstanceWrapper>,
provider: ExistingProvider,
collection: Map<Function | string | symbol, InstanceWrapper>,
) {
const { name, useExisting } = provider;
const { useExisting, provide: providerToken } = provider;
collection.set(
name as string,
providerToken,
new InstanceWrapper({
name,
token: providerToken,
name: (providerToken as Function)?.name || providerToken,
metatype: (instance => instance) as any,
instance: null,
isResolved: false,
@@ -358,9 +377,9 @@ export class Module {
}
public addExportedProvider(
provider: (Provider & ProviderName) | string | symbol | DynamicModule,
provider: Provider | string | symbol | DynamicModule,
) {
const addExportedUnit = (token: string | symbol) =>
const addExportedUnit = (token: InstanceToken) =>
this._exports.add(this.validateExportedProvider(token));
if (this.isCustomProvider(provider as any)) {
@@ -371,7 +390,7 @@ export class Module {
const { module } = provider;
return addExportedUnit(module.name);
}
addExportedUnit(provider.name);
addExportedUnit(provider as Type<any>);
}
public addCustomExportedProvider(
@@ -385,10 +404,10 @@ export class Module {
if (isString(provide) || isSymbol(provide)) {
return this._exports.add(this.validateExportedProvider(provide));
}
this._exports.add(this.validateExportedProvider(provide.name));
this._exports.add(this.validateExportedProvider(provide));
}
public validateExportedProvider(token: string | symbol) {
public validateExportedProvider(token: InstanceToken) {
if (this._providers.has(token)) {
return token;
}
@@ -402,15 +421,17 @@ export class Module {
if (!importsNames.includes(token as string)) {
const { name } = this.metatype;
throw new UnknownExportException(token, name);
const providerName = isFunction(token) ? (token as Function).name : token;
throw new UnknownExportException(providerName as string, name);
}
return token;
}
public addController(controller: Type<Controller>) {
this._controllers.set(
controller.name,
controller,
new InstanceWrapper({
token: controller,
name: controller.name,
metatype: controller,
instance: null,
@@ -436,15 +457,13 @@ export class Module {
this._imports.add(module);
}
public replace(toReplace: string | symbol | Type<any>, options: any) {
public replace(toReplace: InstanceToken, options: any) {
if (options.isProvider && this.hasProvider(toReplace)) {
const name = this.getProviderStaticToken(toReplace);
const originalProvider = this._providers.get(name);
const originalProvider = this._providers.get(toReplace);
return originalProvider.mergeWith({ provide: toReplace, ...options });
} else if (!options.isProvider && this.hasInjectable(toReplace)) {
const name = this.getProviderStaticToken(toReplace);
const originalInjectable = this._injectables.get(name);
const originalInjectable = this._injectables.get(toReplace);
return originalInjectable.mergeWith({
provide: toReplace,
@@ -453,29 +472,21 @@ export class Module {
}
}
public hasProvider(token: string | symbol | Type<any>): boolean {
const name = this.getProviderStaticToken(token);
return this._providers.has(name);
public hasProvider(token: InstanceToken): boolean {
return this._providers.has(token);
}
public hasInjectable(token: string | symbol | Type<any>): boolean {
const name = this.getProviderStaticToken(token);
return this._injectables.has(name);
public hasInjectable(token: InstanceToken): boolean {
return this._injectables.has(token);
}
public getProviderStaticToken(
provider: string | symbol | Type<any> | Abstract<any>,
): string | symbol {
return isFunction(provider)
? (provider as Function).name
: (provider as string | symbol);
}
public getProviderByKey<T = any>(name: string | symbol): InstanceWrapper<T> {
public getProviderByKey<T = any>(name: InstanceToken): InstanceWrapper<T> {
return this._providers.get(name) as InstanceWrapper<T>;
}
public getNonAliasProviders(): Array<[string, InstanceWrapper<Injectable>]> {
public getNonAliasProviders(): Array<
[InstanceToken, InstanceWrapper<Injectable>]
> {
return [...this._providers].filter(([_, wrapper]) => !wrapper.isAlias);
}

View File

@@ -1,3 +1,10 @@
import { v4 as uuid } from 'uuid';
import { Module } from './module';
export class ModulesContainer extends Map<string, Module> {}
export class ModulesContainer extends Map<string, Module> {
private readonly _applicationId = uuid();
get applicationId(): string {
return this._applicationId;
}
}

View File

@@ -1,5 +1,5 @@
import { INTERCEPTORS_METADATA } from '@nestjs/common/constants';
import { Controller, NestInterceptor } from '@nestjs/common/interfaces';
import { Controller, NestInterceptor, Type } from '@nestjs/common/interfaces';
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { ApplicationConfig } from '../application-config';
@@ -59,15 +59,17 @@ export class InterceptorsContextCreator extends ContextCreator {
}
public getInterceptorInstance(
interceptor: Function | NestInterceptor,
metatype: Function | NestInterceptor,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): NestInterceptor | null {
const isObject = (interceptor as NestInterceptor).intercept;
const isObject = (metatype as NestInterceptor).intercept;
if (isObject) {
return interceptor as NestInterceptor;
return metatype as NestInterceptor;
}
const instanceWrapper = this.getInstanceByMetatype(interceptor);
const instanceWrapper = this.getInstanceByMetatype(
metatype as Type<unknown>,
);
if (!instanceWrapper) {
return null;
}
@@ -78,8 +80,8 @@ export class InterceptorsContextCreator extends ContextCreator {
return instanceHost && instanceHost.instance;
}
public getInstanceByMetatype<T extends Record<string, any> = any>(
metatype: T,
public getInstanceByMetatype(
metatype: Type<unknown>,
): InstanceWrapper | undefined {
if (!this.moduleContext) {
return;
@@ -89,7 +91,7 @@ export class InterceptorsContextCreator extends ContextCreator {
if (!moduleRef) {
return;
}
return moduleRef.injectables.get(metatype.name as string);
return moduleRef.injectables.get(metatype);
}
public getGlobalMetadata<T extends unknown[]>(

View File

@@ -9,9 +9,10 @@ import {
RouteInfo,
} from '@nestjs/common/interfaces/middleware';
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
import { iterate } from 'iterare';
import { NestContainer } from '../injector';
import { RoutesMapper } from './routes-mapper';
import { filterMiddleware } from './utils';
import { iterate } from 'iterare';
export class MiddlewareBuilder implements MiddlewareConsumer {
private readonly middlewareCollection = new Set<MiddlewareConfiguration>();
@@ -19,6 +20,7 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
constructor(
private readonly routesMapper: RoutesMapper,
private readonly httpAdapter: HttpServer,
private readonly container: NestContainer,
) {}
public apply(

View File

@@ -3,9 +3,13 @@ import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
import { NestContainer } from '../injector';
import { InstanceWrapper } from '../injector/instance-wrapper';
import { InstanceToken } from '../injector/module';
export class MiddlewareContainer {
private readonly middleware = new Map<string, Map<string, InstanceWrapper>>();
private readonly middleware = new Map<
string,
Map<InstanceToken, InstanceWrapper>
>();
private readonly configurationSets = new Map<
string,
Set<MiddlewareConfiguration>
@@ -15,7 +19,7 @@ export class MiddlewareContainer {
public getMiddlewareCollection(
moduleKey: string,
): Map<string, InstanceWrapper> {
): Map<InstanceToken, InstanceWrapper> {
if (!this.middleware.has(moduleKey)) {
const moduleRef = this.container.getModuleByKey(moduleKey);
this.middleware.set(moduleKey, moduleRef.middlewares);
@@ -36,13 +40,14 @@ export class MiddlewareContainer {
const configurations = configList || [];
const insertMiddleware = <T extends Type<unknown>>(metatype: T) => {
const token = metatype.name;
const token = metatype;
middleware.set(
token,
new InstanceWrapper({
scope: this.getClassScope(metatype),
metatype,
name: token,
metatype,
token,
}),
);
};

View File

@@ -5,7 +5,6 @@ import {
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 {
addLeadingSlash,
isUndefined,
@@ -19,7 +18,7 @@ import { STATIC_CONTEXT } from '../injector/constants';
import { NestContainer } from '../injector/container';
import { Injector } from '../injector/injector';
import { InstanceWrapper } from '../injector/instance-wrapper';
import { Module } from '../injector/module';
import { InstanceToken, Module } from '../injector/module';
import { REQUEST_CONTEXT_ID } from '../router/request/request-constants';
import { RouterExceptionFilters } from '../router/router-exception-filters';
import { RouterProxy } from '../router/router-proxy';
@@ -70,28 +69,29 @@ export class MiddlewareModule {
modules: Map<string, Module>,
) {
const moduleEntries = [...modules.entries()];
const loadMiddlewareConfiguration = async ([name, module]: [
const loadMiddlewareConfiguration = async ([moduleName, moduleRef]: [
string,
Module,
]) => {
const instance = module.instance;
await this.loadConfiguration(middlewareContainer, instance, name);
await this.resolver.resolveInstances(module, name);
await this.loadConfiguration(middlewareContainer, moduleRef, moduleName);
await this.resolver.resolveInstances(moduleRef, moduleName);
};
await Promise.all(moduleEntries.map(loadMiddlewareConfiguration));
}
public async loadConfiguration(
middlewareContainer: MiddlewareContainer,
instance: NestModule,
moduleRef: Module,
moduleKey: string,
) {
const { instance } = moduleRef;
if (!instance.configure) {
return;
}
const middlewareBuilder = new MiddlewareBuilder(
this.routesMapper,
this.httpAdapter,
this.container,
);
await instance.configure(middlewareBuilder);
@@ -164,7 +164,7 @@ export class MiddlewareModule {
for (const metatype of middlewareCollection) {
const collection = middlewareContainer.getMiddlewareCollection(moduleKey);
const instanceWrapper = collection.get(metatype.name);
const instanceWrapper = collection.get(metatype);
if (isUndefined(instanceWrapper)) {
throw new RuntimeException();
}
@@ -188,10 +188,10 @@ export class MiddlewareModule {
method: RequestMethod,
path: string,
moduleRef: Module,
collection: Map<string, InstanceWrapper>,
collection: Map<InstanceToken, InstanceWrapper>,
) {
const { instance, metatype } = wrapper;
if (isUndefined(instance.use)) {
if (isUndefined(instance?.use)) {
throw new InvalidMiddlewareException(metatype.name);
}
const router = await applicationRef.createMiddlewareFactory(method);

View File

@@ -1,6 +1,6 @@
import { Injector } from '../injector/injector';
import { InstanceWrapper } from '../injector/instance-wrapper';
import { Module } from '../injector/module';
import { InstanceToken, Module } from '../injector/module';
import { MiddlewareContainer } from './container';
export class MiddlewareResolver {
@@ -19,7 +19,7 @@ export class MiddlewareResolver {
private async resolveMiddlewareInstance(
wrapper: InstanceWrapper,
middleware: Map<string, InstanceWrapper>,
middleware: Map<InstanceToken, InstanceWrapper>,
moduleRef: Module,
) {
await this.instanceLoader.loadMiddleware(wrapper, middleware, moduleRef);

View File

@@ -1,5 +1,5 @@
import { RequestMethod } from '@nestjs/common';
import { PATH_METADATA } from '@nestjs/common/constants';
import { MODULE_PATH, PATH_METADATA } from '@nestjs/common/constants';
import { RouteInfo, Type } from '@nestjs/common/interfaces';
import {
addLeadingSlash,
@@ -7,13 +7,15 @@ import {
isUndefined,
} from '@nestjs/common/utils/shared.utils';
import { NestContainer } from '../injector/container';
import { Module } from '../injector/module';
import { MetadataScanner } from '../metadata-scanner';
import { RouterExplorer } from '../router/router-explorer';
import { targetModulesByContainer } from '../router/router-module';
export class RoutesMapper {
private readonly routerExplorer: RouterExplorer;
constructor(container: NestContainer) {
constructor(private readonly container: NestContainer) {
this.routerExplorer = new RouterExplorer(new MetadataScanner(), container);
}
@@ -23,35 +25,41 @@ export class RoutesMapper {
if (isString(route)) {
return [
{
path: this.validateRoutePath(route),
path: addLeadingSlash(route),
method: RequestMethod.ALL,
},
];
}
const routePath: string = Reflect.getMetadata(PATH_METADATA, route);
const routePath = this.getRoutePath(route);
if (this.isRouteInfo(routePath, route)) {
return [
{
path: this.validateRoutePath(route.path),
path: addLeadingSlash(route.path),
method: route.method,
},
];
}
const paths = this.routerExplorer.scanForPaths(
const controllerPaths = this.routerExplorer.scanForPaths(
Object.create(route),
route.prototype,
);
const moduleRef = this.getHostModuleOfController(route);
const modulePath = this.getModulePath(moduleRef?.metatype);
const concatPaths = <T>(acc: T[], currentValue: T[]) =>
acc.concat(currentValue);
return paths
.map(
item =>
item.path &&
item.path.map(p => ({
path:
this.validateGlobalPath(routePath) + this.validateRoutePath(p),
return controllerPaths
.map(item =>
item.path?.map(p => {
let path = modulePath ?? '';
path += this.normalizeGlobalPath(routePath) + addLeadingSlash(p);
return {
path,
method: item.requestMethod,
})),
};
}),
)
.reduce(concatPaths, []);
}
@@ -63,12 +71,44 @@ export class RoutesMapper {
return isUndefined(path);
}
private validateGlobalPath(path: string): string {
private normalizeGlobalPath(path: string): string {
const prefix = addLeadingSlash(path);
return prefix === '/' ? '' : prefix;
}
private validateRoutePath(path: string): string {
return addLeadingSlash(path);
private getRoutePath(route: Type<any> | RouteInfo): string | undefined {
return Reflect.getMetadata(PATH_METADATA, route);
}
private getHostModuleOfController(
metatype: Type<unknown>,
): Module | undefined {
if (!metatype?.name) {
return;
}
const modulesContainer = this.container.getModules();
const moduleRefsSet = targetModulesByContainer.get(modulesContainer);
if (!moduleRefsSet) {
return;
}
const modules = Array.from(modulesContainer.values()).filter(moduleRef =>
moduleRefsSet.has(moduleRef),
);
return modules.find(({ routes }) => routes.has(metatype.name));
}
private getModulePath(
metatype: Type<unknown> | undefined,
): string | undefined {
if (!metatype) {
return;
}
const modulesContainer = this.container.getModules();
const modulePath = Reflect.getMetadata(
MODULE_PATH + modulesContainer.applicationId,
metatype,
);
return modulePath ?? Reflect.getMetadata(MODULE_PATH, metatype);
}
}

View File

@@ -39,6 +39,7 @@ export class NestApplicationContext implements INestApplicationContext {
private readonly moduleCompiler = new ModuleCompiler();
private shutdownCleanupRef?: (...args: unknown[]) => unknown;
private _instanceLinksHost: InstanceLinksHost;
private _moduleRefsByDistance?: Array<Module>;
private get instanceLinksHost() {
if (!this._instanceLinksHost) {
@@ -133,6 +134,10 @@ export class NestApplicationContext implements INestApplicationContext {
Logger.overrideLogger(logger);
}
public flushLogs() {
Logger.flush();
}
/**
* Enables the usage of shutdown hooks. Will call the
* `onApplicationShutdown` function of a provider if the
@@ -218,8 +223,8 @@ export class NestApplicationContext implements INestApplicationContext {
* modules and its children.
*/
protected async callInitHook(): Promise<void> {
const modulesContainer = this.container.getModules();
for (const module of [...modulesContainer.values()].reverse()) {
const modulesSortedByDistance = this.getModulesSortedByDistance();
for (const module of modulesSortedByDistance) {
await callModuleInitHook(module);
}
}
@@ -229,8 +234,8 @@ export class NestApplicationContext implements INestApplicationContext {
* modules and its children.
*/
protected async callDestroyHook(): Promise<void> {
const modulesContainer = this.container.getModules();
for (const module of modulesContainer.values()) {
const modulesSortedByDistance = this.getModulesSortedByDistance();
for (const module of modulesSortedByDistance) {
await callModuleDestroyHook(module);
}
}
@@ -240,8 +245,8 @@ export class NestApplicationContext implements INestApplicationContext {
* modules and its children.
*/
protected async callBootstrapHook(): Promise<void> {
const modulesContainer = this.container.getModules();
for (const module of [...modulesContainer.values()].reverse()) {
const modulesSortedByDistance = this.getModulesSortedByDistance();
for (const module of modulesSortedByDistance) {
await callModuleBootstrapHook(module);
}
}
@@ -251,8 +256,8 @@ export class NestApplicationContext implements INestApplicationContext {
* modules and children.
*/
protected async callShutdownHook(signal?: string): Promise<void> {
const modulesContainer = this.container.getModules();
for (const module of [...modulesContainer.values()].reverse()) {
const modulesSortedByDistance = this.getModulesSortedByDistance();
for (const module of modulesSortedByDistance) {
await callAppShutdownHook(module, signal);
}
}
@@ -262,8 +267,8 @@ export class NestApplicationContext implements INestApplicationContext {
* modules and children.
*/
protected async callBeforeShutdownHook(signal?: string): Promise<void> {
const modulesContainer = this.container.getModules();
for (const module of [...modulesContainer.values()].reverse()) {
const modulesSortedByDistance = this.getModulesSortedByDistance();
for (const module of modulesSortedByDistance) {
await callBeforeAppShutdownHook(module, signal);
}
}
@@ -314,4 +319,17 @@ export class NestApplicationContext implements INestApplicationContext {
}
return instance;
}
private getModulesSortedByDistance(): Module[] {
if (this._moduleRefsByDistance) {
return this._moduleRefsByDistance;
}
const modulesContainer = this.container.getModules();
const compareFn = (a: Module, b: Module) => b.distance - a.distance;
this._moduleRefsByDistance = Array.from(modulesContainer.values()).sort(
compareFn,
);
return this._moduleRefsByDistance;
}
}

View File

@@ -16,7 +16,11 @@ import {
import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface';
import { Logger } from '@nestjs/common/services/logger.service';
import { loadPackage } from '@nestjs/common/utils/load-package.util';
import { addLeadingSlash, isObject } from '@nestjs/common/utils/shared.utils';
import {
addLeadingSlash,
isFunction,
isObject,
} from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { platform } from 'os';
import { AbstractHttpAdapter } from './adapters';
@@ -46,7 +50,9 @@ const {
export class NestApplication
extends NestApplicationContext
implements INestApplication {
private readonly logger = new Logger(NestApplication.name, true);
private readonly logger = new Logger(NestApplication.name, {
timestamp: true,
});
private readonly middlewareModule = new MiddlewareModule();
private readonly middlewareContainer = new MiddlewareContainer(
this.container,
@@ -215,15 +221,16 @@ export class NestApplication
return this.httpServer;
}
public startAllMicroservices(callback?: () => void): this {
Promise.all(this.microservices.map(this.listenToPromise)).then(
() => callback && callback(),
);
public async startAllMicroservices(): Promise<this> {
await Promise.all(this.microservices.map(msvc => msvc.listen()));
return this;
}
public startAllMicroservicesAsync(): Promise<void> {
return new Promise(resolve => this.startAllMicroservices(resolve));
public startAllMicroservicesAsync(): Promise<this> {
this.logger.warn(
'DEPRECATED! "startAllMicroservicesAsync" method is deprecated and will be removed in the next major release. Please, use "startAllMicroservices" instead.',
);
return this.startAllMicroservices();
}
public use(...args: [any, any?]): this {
@@ -235,26 +242,49 @@ export class NestApplication
this.httpAdapter.enableCors(options);
}
public async listen(
port: number | string,
callback?: () => void,
): Promise<any>;
public async listen(
port: number | string,
hostname: string,
callback?: () => void,
): Promise<any>;
public async listen(port: number | string): Promise<any>;
public async listen(port: number | string, hostname: string): Promise<any>;
public async listen(port: number | string, ...args: any[]): Promise<any> {
!this.isInitialized && (await this.init());
this.isListening = true;
this.httpAdapter.listen(port, ...args);
return this.httpServer;
return new Promise((resolve, reject) => {
const errorHandler = (e: any) => {
this.logger.error(e?.toString?.());
reject(e);
};
this.httpServer.once('error', errorHandler);
const isCallbackInOriginalArgs = isFunction(args[args.length - 1]);
const listenFnArgs = isCallbackInOriginalArgs
? args.slice(0, args.length - 1)
: args;
this.httpAdapter.listen(
port,
...listenFnArgs,
(...originalCallbackArgs: unknown[]) => {
if (this.appOptions?.autoFlushLogs) {
this.flushLogs();
}
const address = this.httpServer.address();
if (address) {
this.httpServer.removeListener('error', errorHandler);
this.isListening = true;
resolve(this.httpServer);
}
if (isCallbackInOriginalArgs) {
args[args.length - 1](...originalCallbackArgs);
}
},
);
});
}
public listenAsync(port: number | string, hostname?: string): Promise<any> {
return new Promise(resolve => {
const server: any = this.listen(port, hostname, () => resolve(server));
});
public listenAsync(port: number | string, ...args: any[]): Promise<any> {
this.logger.warn(
'DEPRECATED! "listenAsync" method is deprecated and will be removed in the next major release. Please, use "listen" instead.',
);
return this.listen(port, ...(args as [any]));
}
public async getUrl(): Promise<string> {
@@ -263,30 +293,33 @@ export class NestApplication
this.logger.error(MESSAGES.CALL_LISTEN_FIRST);
reject(MESSAGES.CALL_LISTEN_FIRST);
}
this.httpServer.on('listening', () => {
const address = this.httpServer.address();
if (typeof address === 'string') {
if (platform() === 'win32') {
return address;
}
const basePath = encodeURIComponent(address);
return `${this.getProtocol()}+unix://${basePath}`;
}
let host = this.host();
if (address && address.family === 'IPv6') {
if (host === '::') {
host = '[::1]';
} else {
host = `[${host}]`;
}
} else if (host === '0.0.0.0') {
host = '127.0.0.1';
}
resolve(`${this.getProtocol()}://${host}:${address.port}`);
});
const address = this.httpServer.address();
resolve(this.formatAddress(address));
});
}
private formatAddress(address: any): string {
if (typeof address === 'string') {
if (platform() === 'win32') {
return address;
}
const basePath = encodeURIComponent(address);
return `${this.getProtocol()}+unix://${basePath}`;
}
let host = this.host();
if (address && address.family === 'IPv6') {
if (host === '::') {
host = '[::1]';
} else {
host = `[${host}]`;
}
} else if (host === '0.0.0.0') {
host = '127.0.0.1';
}
return `${this.getProtocol()}://${host}:${address.port}`;
}
public setGlobalPrefix(prefix: string): this {
this.config.setGlobalPrefix(prefix);
return this;
@@ -353,10 +386,4 @@ export class NestApplication
instance,
);
}
private listenToPromise(microservice: INestMicroservice) {
return new Promise<void>(async resolve => {
await microservice.listen(resolve);
});
}
}

View File

@@ -27,8 +27,12 @@ import { DependenciesScanner } from './scanner';
* @publicApi
*/
export class NestFactoryStatic {
private readonly logger = new Logger('NestFactory', true);
private readonly logger = new Logger('NestFactory', {
timestamp: true,
});
private abortOnError = true;
private autoFlushLogs = false;
/**
* Creates an instance of NestApplication.
*
@@ -70,7 +74,8 @@ export class NestFactoryStatic {
const applicationConfig = new ApplicationConfig();
const container = new NestContainer(applicationConfig);
this.setAbortOnError(serverOrOptions, options);
this.applyLogger(appOptions);
this.registerLoggerConfiguration(appOptions);
await this.initialize(module, container, applicationConfig, httpServer);
const instance = new NestApplication(
@@ -104,7 +109,7 @@ export class NestFactoryStatic {
const applicationConfig = new ApplicationConfig();
const container = new NestContainer(applicationConfig);
this.setAbortOnError(options);
this.applyLogger(options);
this.registerLoggerConfiguration(options);
await this.initialize(module, container, applicationConfig);
return this.createNestInstance<INestMicroservice>(
@@ -127,10 +132,13 @@ export class NestFactoryStatic {
): Promise<INestApplicationContext> {
const container = new NestContainer();
this.setAbortOnError(options);
this.applyLogger(options);
this.registerLoggerConfiguration(options);
await this.initialize(module, container);
const modules = container.getModules().values();
const root = modules.next().value;
const context = this.createNestInstance<NestApplicationContext>(
new NestApplicationContext(container, [], root),
);
@@ -161,11 +169,15 @@ export class NestFactoryStatic {
try {
this.logger.log(MESSAGES.APPLICATION_START);
await ExceptionsZone.asyncRun(async () => {
await dependenciesScanner.scan(module);
await instanceLoader.createInstancesOfDependencies();
dependenciesScanner.applyApplicationProviders();
}, teardown);
await ExceptionsZone.asyncRun(
async () => {
await dependenciesScanner.scan(module);
await instanceLoader.createInstancesOfDependencies();
dependenciesScanner.applyApplicationProviders();
},
teardown,
this.autoFlushLogs,
);
} catch (e) {
this.handleInitializationError(e);
}
@@ -214,11 +226,20 @@ export class NestFactoryStatic {
};
}
private applyLogger(options: NestApplicationContextOptions | undefined) {
if (!options || options?.logger === true || isNil(options?.logger)) {
private registerLoggerConfiguration(
options: NestApplicationContextOptions | undefined,
) {
if (!options) {
return;
}
Logger.overrideLogger(options.logger);
const { logger, bufferLogs, autoFlushLogs } = options;
if (logger !== true && !isNil(logger)) {
Logger.overrideLogger(logger);
}
if (bufferLogs) {
Logger.attachBuffer();
}
this.autoFlushLogs = autoFlushLogs;
}
private createHttpAdapter<T = any>(httpServer?: T): AbstractHttpAdapter {

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/core",
"version": "7.6.6",
"version": "7.6.7",
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -36,7 +36,7 @@
"uuid": "8.3.2"
},
"devDependencies": {
"@nestjs/common": "7.6.6"
"@nestjs/common": "7.6.7"
},
"peerDependencies": {
"@nestjs/common": "^7.0.0",

View File

@@ -1,5 +1,5 @@
import { PIPES_METADATA } from '@nestjs/common/constants';
import { Controller, PipeTransform } from '@nestjs/common/interfaces';
import { Controller, PipeTransform, Type } from '@nestjs/common/interfaces';
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { ApplicationConfig } from '../application-config';
@@ -59,7 +59,7 @@ export class PipesContextCreator extends ContextCreator {
if (isObject) {
return pipe as PipeTransform;
}
const instanceWrapper = this.getInstanceByMetatype(pipe as Function);
const instanceWrapper = this.getInstanceByMetatype(pipe as Type<unknown>);
if (!instanceWrapper) {
return null;
}
@@ -70,8 +70,8 @@ export class PipesContextCreator extends ContextCreator {
return instanceHost && instanceHost.instance;
}
public getInstanceByMetatype<T extends Record<'name', string> = any>(
metatype: T,
public getInstanceByMetatype(
metatype: Type<unknown>,
): InstanceWrapper | undefined {
if (!this.moduleContext) {
return;
@@ -81,7 +81,7 @@ export class PipesContextCreator extends ContextCreator {
if (!moduleRef) {
return;
}
return moduleRef.injectables.get(metatype.name);
return moduleRef.injectables.get(metatype);
}
public getGlobalMetadata<T extends unknown[]>(

View File

@@ -1 +1,3 @@
export * from './interfaces';
export * from './request';
export { RouterModule } from './router-module';

View File

@@ -0,0 +1 @@
export * from './routes.interface';

View File

@@ -0,0 +1,9 @@
import { Type } from '@nestjs/common';
export interface RouteTree {
path: string;
module?: Type<any>;
children?: Routes | Type<any>[];
}
export type Routes = RouteTree[];

View File

@@ -45,7 +45,9 @@ export interface RoutePathProperties {
export class RouterExplorer {
private readonly executionContextCreator: RouterExecutionContext;
private readonly routerMethodFactory = new RouterMethodFactory();
private readonly logger = new Logger(RouterExplorer.name, true);
private readonly logger = new Logger(RouterExplorer.name, {
timestamp: true,
});
private readonly exceptionFiltersCache = new WeakMap();
constructor(
@@ -99,8 +101,7 @@ export class RouterExplorer {
} else {
path = [prefix + addLeadingSlash(path)];
}
return path.map(p => addLeadingSlash(p));
return path.map((p: string) => addLeadingSlash(p));
}
public scanForPaths(
@@ -135,7 +136,7 @@ export class RouterExplorer {
);
const path = isString(routePath)
? [addLeadingSlash(routePath)]
: routePath.map(p => addLeadingSlash(p));
: routePath.map((p: string) => addLeadingSlash(p));
return {
path,
requestMethod,

View File

@@ -0,0 +1,76 @@
import { DynamicModule, Inject, Module, Type } from '@nestjs/common';
import { MODULE_PATH } from '@nestjs/common/constants';
import { normalizePath } from '@nestjs/common/utils/shared.utils';
import { ModulesContainer } from '../injector';
import { Module as ModuleClass } from '../injector/module';
import { Routes } from './interfaces';
import { flattenRoutePaths } from './utils';
export const ROUTES = Symbol('ROUTES');
export const targetModulesByContainer = new WeakMap<
ModulesContainer,
WeakSet<ModuleClass>
>();
/**
* @publicApi
*/
@Module({})
export class RouterModule {
constructor(
private readonly modulesContainer: ModulesContainer,
@Inject(ROUTES) private readonly routes: Routes,
) {
this.initialize();
}
static register(routes: Routes): DynamicModule {
return {
module: RouterModule,
providers: [
{
provide: ROUTES,
useValue: routes,
},
],
};
}
private initialize() {
const flattenedRoutes = flattenRoutePaths(this.routes);
flattenedRoutes.forEach(route => {
const modulePath = normalizePath(route.path);
this.registerModulePathMetadata(route.module, modulePath);
this.updateTargetModulesCache(route.module);
});
}
private registerModulePathMetadata(
moduleCtor: Type<unknown>,
modulePath: string,
) {
Reflect.defineMetadata(
MODULE_PATH + this.modulesContainer.applicationId,
modulePath,
moduleCtor,
);
}
private updateTargetModulesCache(moduleCtor: Type<unknown>) {
let moduleClassSet: WeakSet<ModuleClass>;
if (targetModulesByContainer.has(this.modulesContainer)) {
moduleClassSet = targetModulesByContainer.get(this.modulesContainer);
} else {
moduleClassSet = new WeakSet<ModuleClass>();
targetModulesByContainer.set(this.modulesContainer, moduleClassSet);
}
const moduleRef = Array.from(this.modulesContainer.values()).find(
item => item?.metatype === moduleCtor,
);
if (!moduleRef) {
return;
}
moduleClassSet.add(moduleRef);
}
}

View File

@@ -15,7 +15,9 @@ import { RouterExplorer } from './router-explorer';
import { RouterProxy } from './router-proxy';
export class RoutesResolver implements Resolver {
private readonly logger = new Logger(RoutesResolver.name, true);
private readonly logger = new Logger(RoutesResolver.name, {
timestamp: true,
});
private readonly routerProxy = new RouterProxy();
private readonly routerExceptionsFilter: RouterExceptionFilters;
private readonly routerExplorer: RouterExplorer;
@@ -51,7 +53,7 @@ export class RoutesResolver implements Resolver {
}
public registerRouters(
routes: Map<string, InstanceWrapper<Controller>>,
routes: Map<string | symbol | Function, InstanceWrapper<Controller>>,
moduleName: string,
basePath: string,
applicationRef: HttpServer,
@@ -127,7 +129,12 @@ export class RoutesResolver implements Resolver {
}
private getModulePathMetadata(metatype: Type<unknown>): string | undefined {
return Reflect.getMetadata(MODULE_PATH, metatype);
const modulesContainer = this.container.getModules();
const modulePath = Reflect.getMetadata(
MODULE_PATH + modulesContainer.applicationId,
metatype,
);
return modulePath ?? Reflect.getMetadata(MODULE_PATH, metatype);
}
private getHostMetadata(

View File

@@ -0,0 +1,25 @@
import { normalizePath } from '@nestjs/common/utils/shared.utils';
import { Routes } from '../interfaces/routes.interface';
export function flattenRoutePaths(routes: Routes) {
const result = [];
routes.forEach(item => {
if (item.module && item.path) {
result.push({ module: item.module, path: item.path });
}
if (item.children) {
const childrenRef = item.children as Routes;
childrenRef.forEach(child => {
if (typeof child !== 'string' && child.path) {
child.path = normalizePath(
normalizePath(item.path) + normalizePath(child.path),
);
} else {
result.push({ path: item.path, module: child });
}
});
result.push(...flattenRoutePaths(childrenRef));
}
});
return result;
}

View File

@@ -0,0 +1 @@
export * from './flatten-route-paths.util';

View File

@@ -282,21 +282,25 @@ export class DependenciesScanner {
public async calculateModulesDistance(modules: ModulesContainer) {
const modulesGenerator = modules.values();
const rootModule = modulesGenerator.next().value as Module;
const modulesStack = [rootModule];
const calculateDistance = (moduleRef: Module, distance = 1) => {
// Skip "InternalCoreModule" from calculating distance
modulesGenerator.next();
const modulesStack = [];
const calculateDistance = (moduleRef: Module, distance = 0) => {
if (modulesStack.includes(moduleRef)) {
return;
}
modulesStack.push(moduleRef);
const moduleImports = rootModule.relatedModules;
moduleImports.forEach(module => {
module.distance = distance;
calculateDistance(module, distance + 1);
const moduleImports = moduleRef.imports;
moduleImports.forEach(importedModuleRef => {
importedModuleRef.distance = distance;
calculateDistance(importedModuleRef, distance + 1);
});
};
const rootModule = modulesGenerator.next().value as Module;
calculateDistance(rootModule);
}
@@ -357,11 +361,10 @@ export class DependenciesScanner {
scope,
} as Provider;
if (
this.isRequestOrTransient(
(newProvider as FactoryProvider | ClassProvider).scope,
)
) {
const factoryOrClassProvider = newProvider as
| FactoryProvider
| ClassProvider;
if (this.isRequestOrTransient(factoryOrClassProvider.scope)) {
return this.container.addInjectable(newProvider, token);
}
this.container.addProvider(newProvider, token);

View File

@@ -1,6 +1,7 @@
import { HttpException } from '@nestjs/common';
import { isNil, isObject } from '@nestjs/common/utils/shared.utils';
import { expect } from 'chai';
import * as createHttpError from 'http-errors';
import * as sinon from 'sinon';
import { AbstractHttpAdapter } from '../../adapters';
import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception';
@@ -13,7 +14,7 @@ describe('ExceptionsHandler', () => {
let handler: ExceptionsHandler;
let statusStub: sinon.SinonStub;
let jsonStub: sinon.SinonStub;
let response;
let response: any;
beforeEach(() => {
adapter = new NoopHttpAdapter({});
@@ -45,7 +46,7 @@ describe('ExceptionsHandler', () => {
: responseRef.send(String(body));
});
});
it('should method send expected response status code and message when exception is unknown', () => {
it('should send expected response status code and message when exception is unknown', () => {
handler.next(new Error(), new ExecutionContextHost([0, response]));
expect(statusStub.calledWith(500)).to.be.true;
@@ -56,8 +57,22 @@ describe('ExceptionsHandler', () => {
}),
).to.be.true;
});
describe('when exception is instance of HttpException', () => {
it('should method send expected response status code and json object', () => {
describe('when exception is instantiated by "http-errors" library', () => {
it('should send expected response status code and message', () => {
const error = new createHttpError.NotFound('User does not exist');
handler.next(error, new ExecutionContextHost([0, response]));
expect(statusStub.calledWith(404)).to.be.true;
expect(
jsonStub.calledWith({
statusCode: 404,
message: 'User does not exist',
}),
).to.be.true;
});
});
describe('when exception is an instance of HttpException', () => {
it('should send expected response status code and json object', () => {
const status = 401;
const message = {
custom: 'Unauthorized',
@@ -70,7 +85,7 @@ describe('ExceptionsHandler', () => {
expect(statusStub.calledWith(status)).to.be.true;
expect(jsonStub.calledWith(message)).to.be.true;
});
it('should method send expected response status code and transform message to json', () => {
it('should send expected response status code and transform message to json', () => {
const status = 401;
const message = 'Unauthorized';
@@ -107,7 +122,7 @@ describe('ExceptionsHandler', () => {
});
describe('invokeCustomFilters', () => {
describe('when filters array is empty', () => {
it('should returns false', () => {
it('should return false', () => {
expect(handler.invokeCustomFilters(null, null)).to.be.false;
});
});
@@ -134,7 +149,7 @@ describe('ExceptionsHandler', () => {
handler.invokeCustomFilters(exception, res as any);
expect(funcSpy.calledWith(exception, res)).to.be.true;
});
it('should returns true', () => {
it('should return true', () => {
expect(handler.invokeCustomFilters(new TestException(), null)).to.be
.true;
});
@@ -144,7 +159,7 @@ describe('ExceptionsHandler', () => {
handler.invokeCustomFilters(new TestException(), null);
expect(funcSpy.notCalled).to.be.true;
});
it('should returns false', () => {
it('should return false', () => {
expect(handler.invokeCustomFilters(new TestException(), null)).to.be
.false;
});

View File

@@ -30,7 +30,7 @@ describe('ExternalExceptionFilterContext', () => {
beforeEach(() => {
sinon.stub(exceptionFilter, 'createContext').returns([]);
});
it('should returns plain ExceptionHandler object', () => {
it('should return plain ExceptionHandler object', () => {
const filter = exceptionFilter.create(
new EmptyMetadata(),
() => ({} as any),
@@ -43,7 +43,7 @@ describe('ExternalExceptionFilterContext', () => {
@UseFilters(new ExceptionFilter())
class WithMetadata {}
it('should returns ExceptionHandler object with exception filters', () => {
it('should return ExceptionHandler object with exception filters', () => {
const filter = exceptionFilter.create(
new WithMetadata(),
() => ({} as any),
@@ -54,7 +54,7 @@ describe('ExternalExceptionFilterContext', () => {
});
});
describe('reflectCatchExceptions', () => {
it('should returns FILTER_CATCH_EXCEPTIONS metadata', () => {
it('should return FILTER_CATCH_EXCEPTIONS metadata', () => {
expect(
exceptionFilter.reflectCatchExceptions(new ExceptionFilter()),
).to.be.eql([CustomException]);
@@ -64,7 +64,7 @@ describe('ExternalExceptionFilterContext', () => {
class InvalidFilter {}
const filters = [new ExceptionFilter(), new InvalidFilter(), 'test'];
it('should returns expected exception filters metadata', () => {
it('should return expected exception filters metadata', () => {
const resolved = exceptionFilter.createConcreteContext(filters as any);
expect(resolved).to.have.length(1);
expect(resolved[0].exceptionMetatypes).to.be.deep.equal([

View File

@@ -24,7 +24,7 @@ describe('ExternalExceptionsHandler', () => {
beforeEach(() => {
sinon.stub(handler, 'invokeCustomFilters').returns(observable$ as any);
});
it('should returns observable', () => {
it('should return observable', () => {
const result = handler.next(new Error(), null);
expect(result).to.be.eql(observable$);
});
@@ -42,7 +42,7 @@ describe('ExternalExceptionsHandler', () => {
});
describe('invokeCustomFilters', () => {
describe('when filters array is empty', () => {
it('should returns identity', () => {
it('should return identity', () => {
expect(handler.invokeCustomFilters(null, null)).to.be.null;
});
});
@@ -67,7 +67,7 @@ describe('ExternalExceptionsHandler', () => {
handler.invokeCustomFilters(exception, null);
expect(funcSpy.calledWith(exception)).to.be.true;
});
it('should returns stream', () => {
it('should return stream', () => {
expect(handler.invokeCustomFilters(new TestException(), null)).to.be
.not.null;
});
@@ -77,7 +77,7 @@ describe('ExternalExceptionsHandler', () => {
handler.invokeCustomFilters(new TestException(), null);
expect(funcSpy.notCalled).to.be.true;
});
it('should returns null', () => {
it('should return null', () => {
expect(handler.invokeCustomFilters(new TestException(), null)).to.be
.null;
});

View File

@@ -1,7 +1,6 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { of } from 'rxjs';
import { GuardsConsumer } from '../../guards/guards-consumer';
import { Observable, of } from 'rxjs';
describe('GuardsConsumer', () => {
let consumer: GuardsConsumer;
@@ -49,7 +48,7 @@ describe('GuardsConsumer', () => {
});
describe('pickResult', () => {
describe('when result is Observable', () => {
it('should returns result', async () => {
it('should return result', async () => {
expect(await consumer.pickResult(of(true))).to.be.true;
});
});

View File

@@ -13,17 +13,24 @@ describe('GuardsContextCreator', () => {
let container: any;
let getSpy: sinon.SinonSpy;
class Guard1 {}
class Guard2 {}
beforeEach(() => {
guards = [
{
name: 'test',
name: 'Guard1',
token: Guard1,
metatype: Guard1,
instance: {
canActivate: () => true,
},
getInstanceByContextId: () => guards[0],
},
{
name: 'test2',
name: 'Guard2',
token: Guard2,
metatype: Guard2,
instance: {
canActivate: () => true,
},
@@ -34,8 +41,8 @@ describe('GuardsContextCreator', () => {
];
getSpy = sinon.stub().returns({
injectables: new Map([
['test', guards[0]],
['test2', guards[1]],
[Guard1, guards[0]],
[Guard2, guards[1]],
]),
});
container = {
@@ -51,7 +58,7 @@ describe('GuardsContextCreator', () => {
});
describe('createConcreteContext', () => {
describe('when `moduleContext` is nil', () => {
it('should returns empty array', () => {
it('should return empty array', () => {
const result = guardsContextCreator.createConcreteContext(guards);
expect(result).to.be.empty;
});
@@ -61,8 +68,9 @@ describe('GuardsContextCreator', () => {
guardsContextCreator['moduleContext'] = 'test';
});
it('should filter metatypes', () => {
const guardTypeRefs = [guards[0].metatype, guards[1].instance];
expect(
guardsContextCreator.createConcreteContext(guards),
guardsContextCreator.createConcreteContext(guardTypeRefs),
).to.have.length(2);
});
});
@@ -112,10 +120,11 @@ describe('GuardsContextCreator', () => {
(guardsContextCreator as any).moduleContext = 'test';
});
describe('and when module exists', () => {
describe('but module does not exist', () => {
it('should return undefined', () => {
expect(guardsContextCreator.getInstanceByMetatype({})).to.be
.undefined;
expect(
guardsContextCreator.getInstanceByMetatype(class RandomModule {}),
).to.be.undefined;
});
});
});

View File

@@ -21,7 +21,7 @@ describe('ContextUtils', () => {
@CustomDecorator() custom,
) {}
}
it('should returns ROUTE_ARGS_METADATA callback metadata', () => {
it('should return ROUTE_ARGS_METADATA callback metadata', () => {
const instance = new TestController();
const metadata = contextUtils.reflectCallbackMetadata(
instance,
@@ -63,7 +63,7 @@ describe('ContextUtils', () => {
});
});
describe('getArgumentsLength', () => {
it('should returns maximum index + 1 (length) placed in array', () => {
it('should return maximum index + 1 (length) placed in array', () => {
const max = 4;
const metadata = {
[RouteParamtypes.REQUEST]: { index: 0 },

View File

@@ -9,6 +9,7 @@ import { GuardsConsumer } from '../../guards/guards-consumer';
import { GuardsContextCreator } from '../../guards/guards-context-creator';
import { ExternalContextCreator } from '../../helpers/external-context-creator';
import { NestContainer } from '../../injector/container';
import { Module } from '../../injector/module';
import { ModulesContainer } from '../../injector/modules-container';
import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer';
import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator';
@@ -50,12 +51,12 @@ describe('ExternalContextCreator', () => {
});
describe('create', () => {
it('should call "getContextModuleName" with expected argument', done => {
const getContextModuleNameSpy = sinon.spy(
const getContextModuleKeySpy = sinon.spy(
contextCreator,
'getContextModuleName',
'getContextModuleKey',
);
contextCreator.create({ foo: 'bar' }, callback as any, '', '', null);
expect(getContextModuleNameSpy.called).to.be.true;
expect(getContextModuleKeySpy.called).to.be.true;
done();
});
describe('returns proxy function', () => {
@@ -104,67 +105,31 @@ describe('ExternalContextCreator', () => {
});
});
});
describe('getContextModuleName', () => {
describe('when constructor name is undefined', () => {
describe('getContextModuleKey', () => {
describe('when constructor is undefined', () => {
it('should return empty string', () => {
expect(contextCreator.getContextModuleName({} as any)).to.be.eql('');
expect(contextCreator.getContextModuleKey(undefined)).to.be.eql('');
});
});
describe('when provider exists', () => {
describe('when module reference provider exists', () => {
it('should return module key', () => {
const modules = new Map();
const providerKey = 'test';
const moduleKey = 'key';
modules.set(moduleKey, {});
const moduleRef = new Module(class {}, modules as any);
modules.set(moduleKey, moduleRef);
(contextCreator as any).modulesContainer = modules;
sinon
.stub(contextCreator, 'getProviderByClassName')
.callsFake(() => true);
sinon.stub(moduleRef, 'hasProvider').callsFake(() => true);
expect(
contextCreator.getContextModuleName({ name: providerKey } as any),
contextCreator.getContextModuleKey({ randomObject: true } as any),
).to.be.eql(moduleKey);
});
});
describe('when provider does not exists', () => {
it('should return empty string', () => {
sinon
.stub(contextCreator, 'getProviderByClassName')
.callsFake(() => false);
expect(contextCreator.getContextModuleName({} as any)).to.be.eql('');
});
});
});
describe('getProviderByClassName', () => {
describe('when provider exists', () => {
it('should return true', () => {
const providers = new Map();
const key = 'test';
providers.set(key, key);
expect(
contextCreator.getProviderByClassName(
{
providers,
} as any,
key,
),
).to.be.true;
});
});
describe('when provider does not exists', () => {
it('should return false', () => {
const providers = new Map();
const key = 'test';
expect(
contextCreator.getProviderByClassName(
{
providers,
} as any,
key,
),
).to.be.false;
expect(contextCreator.getContextModuleKey({} as any)).to.be.eql('');
});
});
});
@@ -243,7 +208,7 @@ describe('ExternalContextCreator', () => {
describe('transformToResult', () => {
describe('when resultOrDeffered', () => {
describe('is Promise', () => {
it('should returns Promise', async () => {
it('should return Promise', async () => {
const value = 100;
expect(
await contextCreator.transformToResult(Promise.resolve(value)),
@@ -252,7 +217,7 @@ describe('ExternalContextCreator', () => {
});
describe('is Observable', () => {
it('should returns Promise', async () => {
it('should return Promise', async () => {
const value = 100;
expect(await contextCreator.transformToResult(of(value))).to.be.eq(
100,
@@ -261,7 +226,7 @@ describe('ExternalContextCreator', () => {
});
describe('is value', () => {
it('should returns Promise', async () => {
it('should return Promise', async () => {
const value = 100;
expect(await contextCreator.transformToResult(value)).to.be.eq(100);
});

View File

@@ -14,6 +14,7 @@ describe('RouterMethodFactory', () => {
patch: () => {},
options: () => {},
head: () => {},
all: () => {},
};
beforeEach(() => {
factory = new RouterMethodFactory();
@@ -22,7 +23,7 @@ describe('RouterMethodFactory', () => {
it('should return proper method', () => {
expect(factory.get(target, RequestMethod.DELETE)).to.equal(target.delete);
expect(factory.get(target, RequestMethod.POST)).to.equal(target.post);
expect(factory.get(target, RequestMethod.ALL)).to.equal(target.use);
expect(factory.get(target, RequestMethod.ALL)).to.equal(target.all);
expect(factory.get(target, RequestMethod.PUT)).to.equal(target.put);
expect(factory.get(target, RequestMethod.GET)).to.equal(target.get);
expect(factory.get(target, RequestMethod.PATCH)).to.equal(target.patch);

View File

@@ -23,7 +23,7 @@ describe('BeforeAppShutdown', () => {
sampleProvider = new SampleProvider();
moduleRef = new Module(SampleModule, new NestContainer());
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule);
moduleWrapperRef.instance = new SampleModule();
moduleRef.addProvider({

View File

@@ -23,7 +23,7 @@ describe('OnApplicationBootstrap', () => {
sampleProvider = new SampleProvider();
moduleRef = new Module(SampleModule, new NestContainer());
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule);
moduleWrapperRef.instance = new SampleModule();
moduleRef.addProvider({

View File

@@ -23,7 +23,7 @@ describe('OnApplicationShutdown', () => {
sampleProvider = new SampleProvider();
moduleRef = new Module(SampleModule, new NestContainer());
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule);
moduleWrapperRef.instance = new SampleModule();
moduleRef.addProvider({

Some files were not shown because too many files have changed in this diff Show More