mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
chore(): resolve merge conflicts
This commit is contained in:
@@ -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',
|
||||
|
||||
77
integration/hello-world/e2e/router-module-middleware.spec.ts
Normal file
77
integration/hello-world/e2e/router-module-middleware.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
99
integration/hello-world/e2e/router-module.spec.ts
Normal file
99
integration/hello-world/e2e/router-module.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('MQTT transport', () => {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('NATS transport', () => {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('REDIS transport', () => {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('Kafka concurrent', function () {
|
||||
apps.push(app);
|
||||
|
||||
// await the start
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
};
|
||||
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('MQTT transport', () => {
|
||||
url: 'mqtt://0.0.0.0:1883',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('NATS transport', () => {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('REDIS transport', () => {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('RabbitMQ transport', () => {
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('RPC transport', () => {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
47
integration/nest-application/listen/e2e/express.spec.ts
Normal file
47
integration/nest-application/listen/e2e/express.spec.ts
Normal 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');
|
||||
}
|
||||
});
|
||||
});
|
||||
46
integration/nest-application/listen/e2e/fastify.spec.ts
Normal file
46
integration/nest-application/listen/e2e/fastify.spec.ts
Normal 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');
|
||||
}
|
||||
});
|
||||
});
|
||||
12
integration/nest-application/listen/src/app.controller.ts
Normal file
12
integration/nest-application/listen/src/app.controller.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
integration/nest-application/listen/src/app.module.ts
Normal file
9
integration/nest-application/listen/src/app.module.ts
Normal 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 {}
|
||||
8
integration/nest-application/listen/src/app.service.ts
Normal file
8
integration/nest-application/listen/src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
sayHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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', {});
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "7.6.6"
|
||||
"version": "7.6.7"
|
||||
}
|
||||
|
||||
34198
package-lock.json
generated
34198
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -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"
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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",
|
||||
|
||||
254
packages/common/services/console-logger.service.ts
Normal file
254
packages/common/services/console-logger.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './console-logger.service';
|
||||
export * from './logger.service';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/common/services/utils/index.ts
Normal file
1
packages/common/services/utils/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './is-log-level-enabled.util';
|
||||
32
packages/common/services/utils/is-log-level-enabled.util.ts
Normal file
32
packages/common/services/utils/is-log-level-enabled.util.ts
Normal 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;
|
||||
}
|
||||
@@ -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' },
|
||||
];
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
502
packages/common/test/services/logger.service.spec.ts
Normal file
502
packages/common/test/services/logger.service.spec.ts
Normal 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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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[]) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[]>(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[]>(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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[]>(
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './interfaces';
|
||||
export * from './request';
|
||||
export { RouterModule } from './router-module';
|
||||
|
||||
1
packages/core/router/interfaces/index.ts
Normal file
1
packages/core/router/interfaces/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './routes.interface';
|
||||
9
packages/core/router/interfaces/routes.interface.ts
Normal file
9
packages/core/router/interfaces/routes.interface.ts
Normal 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[];
|
||||
@@ -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,
|
||||
|
||||
76
packages/core/router/router-module.ts
Normal file
76
packages/core/router/router-module.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
25
packages/core/router/utils/flatten-route-paths.util.ts
Normal file
25
packages/core/router/utils/flatten-route-paths.util.ts
Normal 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;
|
||||
}
|
||||
1
packages/core/router/utils/index.ts
Normal file
1
packages/core/router/utils/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './flatten-route-paths.util';
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user