mirror of
https://github.com/nestjs/nest.git
synced 2026-02-24 00:02:56 +00:00
Compare commits
7 Commits
kaufmo-add
...
andrewda-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4804f32472 | ||
|
|
40a0678fbf | ||
|
|
6b119c3579 | ||
|
|
f22d3370f3 | ||
|
|
c12100c3c6 | ||
|
|
8cf29c9172 | ||
|
|
dd51cf8f51 |
@@ -33,7 +33,6 @@ 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',
|
||||
|
||||
@@ -63,6 +63,22 @@ describe('Hello world (fastify adapter)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it(`/GET { host: [":tenant.example1.com", ":tenant.example2.com"] } not matched`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/host-array',
|
||||
})
|
||||
.then(({ payload }) => {
|
||||
expect(JSON.parse(payload)).to.be.eql({
|
||||
error: 'Internal Server Error',
|
||||
message:
|
||||
'HTTP adapter does not support filtering on hosts: [":tenant.example1.com", ":tenant.example2.com"]',
|
||||
statusCode: 500,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`/GET inject with LightMyRequest chaining API`, () => {
|
||||
return app
|
||||
.inject()
|
||||
|
||||
@@ -28,6 +28,16 @@ describe('Hello world (default adapter)', () => {
|
||||
path: '/host',
|
||||
greeting: 'Host Greeting! tenant=acme',
|
||||
},
|
||||
{
|
||||
host: 'acme.example1.com',
|
||||
path: '/host-array',
|
||||
greeting: 'Host Greeting! tenant=acme',
|
||||
},
|
||||
{
|
||||
host: 'acme.example2.com',
|
||||
path: '/host-array',
|
||||
greeting: 'Host Greeting! tenant=acme',
|
||||
},
|
||||
].forEach(({ host, path, greeting }) => {
|
||||
describe(`host=${host}`, () => {
|
||||
describe('/GET', () => {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,99 +0,0 @@
|
||||
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,8 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HelloModule } from './hello/hello.module';
|
||||
import { HostArrayModule } from './host-array/host-array.module';
|
||||
import { HostModule } from './host/host.module';
|
||||
|
||||
@Module({
|
||||
imports: [HelloModule, HostModule],
|
||||
imports: [HelloModule, HostModule, HostArrayModule],
|
||||
})
|
||||
export class ApplicationModule {}
|
||||
|
||||
10
integration/hello-world/src/host-array/dto/test.dto.ts
Normal file
10
integration/hello-world/src/host-array/dto/test.dto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IsString, IsNotEmpty, IsNumber } from 'class-validator';
|
||||
|
||||
export class TestDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
string: string;
|
||||
|
||||
@IsNumber()
|
||||
number: number;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Controller, Get, Header, HostParam, Param } from '@nestjs/common';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HostArrayService } from './host-array.service';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
|
||||
@Controller({
|
||||
path: 'host-array',
|
||||
host: [':tenant.example1.com', ':tenant.example2.com'],
|
||||
})
|
||||
export class HostArrayController {
|
||||
constructor(private readonly hostService: HostArrayService) {}
|
||||
|
||||
@Get()
|
||||
@Header('Authorization', 'Bearer')
|
||||
greeting(@HostParam('tenant') tenant: string): string {
|
||||
return `${this.hostService.greeting()} tenant=${tenant}`;
|
||||
}
|
||||
|
||||
@Get('async')
|
||||
async asyncGreeting(@HostParam('tenant') tenant: string): Promise<string> {
|
||||
return `${await this.hostService.greeting()} tenant=${tenant}`;
|
||||
}
|
||||
|
||||
@Get('stream')
|
||||
streamGreeting(@HostParam('tenant') tenant: string): Observable<string> {
|
||||
return of(`${this.hostService.greeting()} tenant=${tenant}`);
|
||||
}
|
||||
|
||||
@Get('local-pipe/:id')
|
||||
localPipe(
|
||||
@Param('id', UserByIdPipe)
|
||||
user: any,
|
||||
@HostParam('tenant') tenant: string,
|
||||
): any {
|
||||
return { ...user, tenant };
|
||||
}
|
||||
}
|
||||
10
integration/hello-world/src/host-array/host-array.module.ts
Normal file
10
integration/hello-world/src/host-array/host-array.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HostArrayController } from './host-array.controller';
|
||||
import { HostArrayService } from './host-array.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HostArrayController],
|
||||
providers: [HostArrayService, UsersService],
|
||||
})
|
||||
export class HostArrayModule {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class HostArrayService {
|
||||
greeting(): string {
|
||||
return 'Host Greeting!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
return this.usersService.findById(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
findById(id: string) {
|
||||
return { id, host: true };
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BeforeApplicationShutdown, Injectable, Module } from '@nestjs/common';
|
||||
import { BeforeApplicationShutdown, Injectable } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
@@ -19,46 +19,35 @@ describe('BeforeApplicationShutdown', () => {
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.beforeApplicationShutdown.called).to.be.true;
|
||||
});
|
||||
|
||||
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 {}
|
||||
|
||||
/*
|
||||
it('should not stop the server once beforeApplicationShutdown has been called', async () => {
|
||||
let resolve;
|
||||
const promise = new Promise(r => (resolve = r));
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [A],
|
||||
providers: [
|
||||
{
|
||||
provide: 'Test',
|
||||
useValue: {
|
||||
beforeApplicationShutdown: () => promise,
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
Sinon.stub(module, 'dispose' as any);
|
||||
const app = module.createNestApplication();
|
||||
await app.init();
|
||||
await app.close();
|
||||
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
});
|
||||
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,
|
||||
);
|
||||
});*/
|
||||
});
|
||||
|
||||
@@ -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,45 +41,4 @@ 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, Module, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
@@ -19,46 +19,4 @@ 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, Module, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Injectable, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
@@ -39,46 +39,4 @@ 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,45 +37,4 @@ 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.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('NATS transport', () => {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('REDIS transport', () => {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('Kafka concurrent', function () {
|
||||
apps.push(app);
|
||||
|
||||
// await the start
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as GRPC from '@grpc/grpc-js';
|
||||
import * as ProtoLoader from '@grpc/proto-loader';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
@@ -7,6 +6,7 @@ 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.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
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.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
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(50000);
|
||||
this.timeout(30000);
|
||||
|
||||
it(`Start Kafka app`, async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
@@ -33,7 +33,7 @@ describe('Kafka transport', function () {
|
||||
},
|
||||
});
|
||||
app.enableShutdownHooks();
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
}).timeout(30000);
|
||||
|
||||
@@ -130,4 +130,4 @@ describe('Kafka transport', function () {
|
||||
after(`Stopping Kafka app`, async () => {
|
||||
await app.close();
|
||||
});
|
||||
}).timeout(50000);
|
||||
}).timeout(30000);
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('MQTT transport', () => {
|
||||
url: 'mqtt://0.0.0.0:1883',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('NATS transport', () => {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('REDIS transport', () => {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('RabbitMQ transport', () => {
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('RPC transport', () => {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
||||
import {
|
||||
Client,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { join } from 'path';
|
||||
import { Observable, of, ReplaySubject, Subject } from 'rxjs';
|
||||
import { Metadata } from 'grpc';
|
||||
|
||||
@Controller()
|
||||
export class AdvancedGrpcController {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
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');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Module({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -1,8 +0,0 @@
|
||||
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.startAllMicroservices();
|
||||
await app.startAllMicroservicesAsync();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { ExpressAdapter, NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
const readmeString = readFileSync(join(process.cwd(), 'Readme.md')).toString();
|
||||
|
||||
describe('Express FileSend', () => {
|
||||
let app: NestExpressApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const modRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = modRef.createNestApplication(new ExpressAdapter());
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should return a file from a stream', async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/file/stream/')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
it('should return a file from a buffer', async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/file/buffer')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
it('should not stream a non-file', async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/non-file/pipe-method')
|
||||
.expect(200)
|
||||
.expect({ value: 'Hello world' });
|
||||
});
|
||||
it('should return a file from an RxJS stream', async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/file/rxjs/stream/')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
const readmeString = readFileSync(join(process.cwd(), 'Readme.md')).toString();
|
||||
|
||||
describe('Fastify FileSend', () => {
|
||||
let app: NestFastifyApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const modRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = modRef.createNestApplication(new FastifyAdapter());
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should return a file from a stream', async () => {
|
||||
return app.inject({
|
||||
method: 'GET',
|
||||
url: '/file/stream'
|
||||
}).then(({ payload }) => {
|
||||
expect(payload.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
it('should return a file from a buffer', async () => {
|
||||
return app.inject({
|
||||
method: 'GET',
|
||||
url: '/file/buffer',
|
||||
}).then(({ payload }) => {
|
||||
expect(payload.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* It seems that Fastify has a similar issue as Kamil initially pointed out
|
||||
* If a class has a `pipe` method, it will be treated as a stream. This means
|
||||
* that the `NonFile` test is a failed case for fastify, hence the skip.
|
||||
*/
|
||||
it.skip('should not stream a non-file', async () => {
|
||||
return app.inject({
|
||||
url: '/non-file/pipe-method',
|
||||
method: 'get'
|
||||
}).then(({ payload }) => {
|
||||
expect(payload).to.be.eq({ value: 'Hello world' });
|
||||
});
|
||||
});
|
||||
it('should return a file from an RxJS stream', async () => {
|
||||
return app.inject({
|
||||
method: 'GET',
|
||||
url: '/file/rxjs/stream'
|
||||
}).then(({ payload }) => {
|
||||
expect(payload.toString()).to.be.eq(readmeString);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Controller, Get, StreamableFile } from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AppService } from './app.service';
|
||||
import { NonFile } from './non-file';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get('file/stream')
|
||||
getFile(): StreamableFile {
|
||||
return this.appService.getReadStream();
|
||||
}
|
||||
|
||||
@Get('file/buffer')
|
||||
getBuffer(): StreamableFile {
|
||||
return this.appService.getBuffer();
|
||||
}
|
||||
|
||||
@Get('non-file/pipe-method')
|
||||
getNonFile(): NonFile {
|
||||
return this.appService.getNonFile();
|
||||
}
|
||||
|
||||
@Get('file/rxjs/stream')
|
||||
getRxJSFile(): Observable<StreamableFile> {
|
||||
return this.appService.getRxJSFile();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Module({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Injectable, StreamableFile } from '@nestjs/common';
|
||||
import { createReadStream, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { NonFile } from './non-file';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getReadStream(): StreamableFile {
|
||||
return new StreamableFile(
|
||||
createReadStream(join(process.cwd(), 'Readme.md')),
|
||||
);
|
||||
}
|
||||
|
||||
getBuffer(): StreamableFile {
|
||||
return new StreamableFile(readFileSync(join(process.cwd(), 'Readme.md')));
|
||||
}
|
||||
|
||||
getNonFile(): NonFile {
|
||||
return new NonFile('Hello world');
|
||||
}
|
||||
|
||||
getRxJSFile(): Observable<StreamableFile> {
|
||||
return of(this.getReadStream());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export class NonFile {
|
||||
constructor(private readonly value: string) {}
|
||||
|
||||
pipe() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { io } from 'socket.io-client';
|
||||
import * as io from 'socket.io-client';
|
||||
import { ErrorGateway } from '../src/error.gateway';
|
||||
|
||||
describe('ErrorGateway', () => {
|
||||
@@ -12,11 +12,11 @@ describe('ErrorGateway', () => {
|
||||
providers: [ErrorGateway],
|
||||
}).compile();
|
||||
app = await testingModule.createNestApplication();
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
});
|
||||
|
||||
it(`should handle error`, async () => {
|
||||
const ws = io('http://localhost:8080');
|
||||
const ws = io.connect('http://localhost:8080');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { io } from 'socket.io-client';
|
||||
import * as io from 'socket.io-client';
|
||||
import { AckGateway } from '../src/ack.gateway';
|
||||
|
||||
async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
const testingModule = await Test.createTestingModule({
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = testingModule.createNestApplication();
|
||||
const app = await testingModule.createNestApplication();
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ describe('WebSocketGateway (ack)', () => {
|
||||
|
||||
it(`should handle message with ack (http)`, async () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
ws = io.connect('http://localhost:8080');
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('push', { test: 'test' }, data => {
|
||||
expect(data).to.be.eql('pong');
|
||||
@@ -30,9 +30,9 @@ describe('WebSocketGateway (ack)', () => {
|
||||
|
||||
it(`should handle message with ack & without data (http)`, async () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
ws = io.connect('http://localhost:8080');
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('push', data => {
|
||||
expect(data).to.be.eql('pong');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { io } from 'socket.io-client';
|
||||
import * as io from 'socket.io-client';
|
||||
import { ApplicationGateway } from '../src/app.gateway';
|
||||
import { NamespaceGateway } from '../src/namespace.gateway';
|
||||
import { ServerGateway } from '../src/server.gateway';
|
||||
@@ -10,7 +10,7 @@ async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
const testingModule = await Test.createTestingModule({
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = testingModule.createNestApplication();
|
||||
const app = await testingModule.createNestApplication();
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ describe('WebSocketGateway', () => {
|
||||
|
||||
it(`should handle message (2nd port)`, async () => {
|
||||
app = await createNestApp(ApplicationGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
ws = io.connect('http://localhost:8080');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
@@ -35,9 +35,9 @@ describe('WebSocketGateway', () => {
|
||||
|
||||
it(`should handle message (http)`, async () => {
|
||||
app = await createNestApp(ServerGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
ws = io('http://localhost:3000');
|
||||
ws = io.connect('http://localhost:3000');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
@@ -51,10 +51,10 @@ describe('WebSocketGateway', () => {
|
||||
|
||||
it(`should handle message (2 gateways)`, async () => {
|
||||
app = await createNestApp(ApplicationGateway, NamespaceGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
io('http://localhost:8080/test').emit('push', {});
|
||||
ws = io.connect('http://localhost:8080');
|
||||
io.connect('http://localhost:8080/test').emit('push', {});
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
|
||||
it(`should handle message (2nd port)`, async () => {
|
||||
app = await createNestApp(ApplicationGateway);
|
||||
await app.listen(3000);
|
||||
await app.listenAsync(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.listen(3000);
|
||||
await app.listenAsync(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.listen(3000);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
// open websockets delay
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
34548
package-lock.json
generated
34548
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
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 --require 'node_modules/reflect-metadata/Reflect.js' --exit",
|
||||
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --retries 3 --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",
|
||||
@@ -66,7 +66,7 @@
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.6.3",
|
||||
"socket.io": "3.1.0",
|
||||
"socket.io": "2.4.1",
|
||||
"tslib": "2.1.0",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
@@ -74,7 +74,6 @@
|
||||
"@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",
|
||||
@@ -87,7 +86,6 @@
|
||||
"@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",
|
||||
@@ -96,8 +94,8 @@
|
||||
"@types/sinon": "9.0.10",
|
||||
"@types/socket.io": "2.1.13",
|
||||
"@types/ws": "7.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.14.1",
|
||||
"@typescript-eslint/parser": "4.14.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.14.2",
|
||||
"@typescript-eslint/parser": "4.14.2",
|
||||
"amqp-connection-manager": "3.2.1",
|
||||
"amqplib": "0.6.0",
|
||||
"apollo-server-express": "2.19.2",
|
||||
@@ -128,13 +126,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": "2.0.0",
|
||||
"json-loader": "0.5.7",
|
||||
@@ -159,7 +157,7 @@
|
||||
"rxjs-compat": "6.6.3",
|
||||
"sinon": "9.2.4",
|
||||
"sinon-chai": "3.5.0",
|
||||
"socket.io-client": "3.1.0",
|
||||
"socket.io-client": "2.4.0",
|
||||
"subscriptions-transport-ws": "0.9.18",
|
||||
"supertest": "6.1.3",
|
||||
"ts-morph": "9.1.0",
|
||||
@@ -184,6 +182,7 @@
|
||||
"packages/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules/",
|
||||
"packages/**/test/**",
|
||||
"packages/**/*.spec.ts",
|
||||
"packages/**/adapters/*.ts",
|
||||
@@ -200,7 +199,7 @@
|
||||
"packages/core/injector/instance-links-host.ts",
|
||||
"packages/common/cache/**/*",
|
||||
"packages/common/serializer/**/*",
|
||||
"packages/common/services/*.ts"
|
||||
"packages/common/services/logger.service.ts"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface ControllerOptions extends ScopeOptions {
|
||||
*
|
||||
* @see [Routing](https://docs.nestjs.com/controllers#routing)
|
||||
*/
|
||||
host?: string;
|
||||
host?: string | string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
PROPERTY_DEPS_METADATA,
|
||||
SELF_DECLARED_DEPS_METADATA,
|
||||
} from '../../constants';
|
||||
import { isUndefined } from '../../utils/shared.utils';
|
||||
import { isFunction, isUndefined } from '../../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* Decorator that marks a constructor parameter as a target for
|
||||
@@ -35,7 +35,9 @@ import { isUndefined } from '../../utils/shared.utils';
|
||||
*/
|
||||
export function Inject<T = any>(token?: T) {
|
||||
return (target: object, key: string | symbol, index?: number) => {
|
||||
const type = token || Reflect.getMetadata('design:type', target, key);
|
||||
token = token || Reflect.getMetadata('design:type', target, key);
|
||||
const type =
|
||||
token && isFunction(token) ? ((token as any) as Function).name : token;
|
||||
|
||||
if (!isUndefined(index)) {
|
||||
let dependencies =
|
||||
|
||||
@@ -46,7 +46,7 @@ export function Injectable(options?: InjectableOptions): ClassDecorator {
|
||||
};
|
||||
}
|
||||
|
||||
export function mixin<T>(mixinClass: Type<T>) {
|
||||
export function mixin(mixinClass: Type<any>) {
|
||||
Object.defineProperty(mixinClass, 'name', {
|
||||
value: uuid(),
|
||||
});
|
||||
|
||||
@@ -316,7 +316,7 @@ export function Query(
|
||||
*
|
||||
* For example:
|
||||
* ```typescript
|
||||
* async create(@Body() createDto: CreateCatDto)
|
||||
* async create(@Body() cat: CreateCatDto)
|
||||
* ```
|
||||
*
|
||||
* @see [Request object](https://docs.nestjs.com/controllers#request-object)
|
||||
@@ -333,7 +333,7 @@ export function Body(): ParameterDecorator;
|
||||
*
|
||||
* For example:
|
||||
* ```typescript
|
||||
* async create(@Body(new ValidationPipe()) createDto: CreateCatDto)
|
||||
* async create(@Body(new ValidationPipe()) cat: CreateCatDto)
|
||||
* ```
|
||||
*
|
||||
* @param pipes one or more pipes - either instances or classes - to apply to
|
||||
|
||||
@@ -40,7 +40,6 @@ export class HttpException extends Error {
|
||||
) {
|
||||
super();
|
||||
this.initMessage();
|
||||
this.initName();
|
||||
}
|
||||
|
||||
public initMessage() {
|
||||
@@ -58,10 +57,6 @@ export class HttpException extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
public initName(): void {
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
|
||||
public getResponse(): string | object {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './streamable-file';
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Readable } from 'stream';
|
||||
|
||||
export class StreamableFile {
|
||||
private readonly stream: Readable;
|
||||
|
||||
constructor(buffer: Buffer);
|
||||
constructor(readable: Readable);
|
||||
constructor(bufferOrReadStream: Buffer | Readable) {
|
||||
if (Buffer.isBuffer(bufferOrReadStream)) {
|
||||
this.stream = new Readable();
|
||||
this.stream.push(bufferOrReadStream);
|
||||
this.stream.push(null);
|
||||
} else if (
|
||||
bufferOrReadStream.pipe &&
|
||||
typeof bufferOrReadStream.pipe === 'function'
|
||||
) {
|
||||
this.stream = bufferOrReadStream;
|
||||
}
|
||||
}
|
||||
|
||||
getStream(): Readable {
|
||||
return this.stream;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ export * from './cache';
|
||||
export * from './decorators';
|
||||
export * from './enums';
|
||||
export * from './exceptions';
|
||||
export * from './file-stream';
|
||||
export * from './http';
|
||||
export {
|
||||
Abstract,
|
||||
|
||||
@@ -41,8 +41,6 @@ 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,20 +13,6 @@ 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,12 +56,6 @@ 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,9 +50,15 @@ 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]
|
||||
@@ -60,13 +66,6 @@ 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.
|
||||
*
|
||||
@@ -122,17 +121,17 @@ export interface INestApplication extends INestApplicationContext {
|
||||
/**
|
||||
* Starts all connected microservices asynchronously.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @param {Function} [callback] Optional callback function
|
||||
* @returns {this}
|
||||
*/
|
||||
startAllMicroservices(): Promise<this>;
|
||||
startAllMicroservices(callback?: () => void): this;
|
||||
|
||||
/**
|
||||
* Starts all connected microservices and can be awaited.
|
||||
* @deprecated use "startAllMicroservices" instead.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
startAllMicroservicesAsync(): Promise<this>;
|
||||
startAllMicroservicesAsync(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 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(): Promise<any>;
|
||||
listen(callback: () => void): void;
|
||||
|
||||
/**
|
||||
* Starts the microservice (can be awaited).
|
||||
* @deprecated use "listen" instead.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,5 @@ export * from './default-value.pipe';
|
||||
export * from './parse-array.pipe';
|
||||
export * from './parse-bool.pipe';
|
||||
export * from './parse-int.pipe';
|
||||
export * from './parse-float.pipe';
|
||||
export * from './parse-enum.pipe';
|
||||
export * from './parse-uuid.pipe';
|
||||
export * from './validation.pipe';
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { ArgumentMetadata, HttpStatus, Injectable, Optional } from '../index';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
|
||||
export interface ParseEnumPipeOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the built-in ParseEnum Pipe
|
||||
*
|
||||
* @see [Built-in Pipes](https://docs.nestjs.com/pipes#built-in-pipes)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@Injectable()
|
||||
export class ParseEnumPipe<T = any> implements PipeTransform<T> {
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(
|
||||
protected readonly enumType: T,
|
||||
@Optional() options?: ParseEnumPipeOptions,
|
||||
) {
|
||||
if (!enumType) {
|
||||
throw new Error(
|
||||
`"ParseEnumPipe" requires "enumType" argument specified (to validate input values).`,
|
||||
);
|
||||
}
|
||||
options = options || {};
|
||||
const {
|
||||
exceptionFactory,
|
||||
errorHttpStatusCode = HttpStatus.BAD_REQUEST,
|
||||
} = options;
|
||||
|
||||
this.exceptionFactory =
|
||||
exceptionFactory ||
|
||||
(error => new HttpErrorByCode[errorHttpStatusCode](error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that accesses and performs optional transformation on argument for
|
||||
* in-flight requests.
|
||||
*
|
||||
* @param value currently processed route argument
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: T, metadata: ArgumentMetadata): Promise<T> {
|
||||
if (!this.isEnum(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (enum string is expected)',
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected isEnum(value: T): boolean {
|
||||
const enumValues = Object.keys(this.enumType).map(
|
||||
item => this.enumType[item],
|
||||
);
|
||||
return enumValues.indexOf(value) >= 0;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { ArgumentMetadata, HttpStatus, Injectable, Optional } from '../index';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
|
||||
export interface ParseFloatPipeOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the built-in ParseFloat Pipe
|
||||
*
|
||||
* @see [Built-in Pipes](https://docs.nestjs.com/pipes#built-in-pipes)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@Injectable()
|
||||
export class ParseFloatPipe implements PipeTransform<string> {
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(@Optional() options?: ParseFloatPipeOptions) {
|
||||
options = options || {};
|
||||
const {
|
||||
exceptionFactory,
|
||||
errorHttpStatusCode = HttpStatus.BAD_REQUEST,
|
||||
} = options;
|
||||
|
||||
this.exceptionFactory =
|
||||
exceptionFactory ||
|
||||
(error => new HttpErrorByCode[errorHttpStatusCode](error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that accesses and performs optional transformation on argument for
|
||||
* in-flight requests.
|
||||
*
|
||||
* @param value currently processed route argument
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
|
||||
const isNumeric =
|
||||
['string', 'number'].includes(typeof value) &&
|
||||
!isNaN(parseFloat(value)) &&
|
||||
isFinite(value as any);
|
||||
if (!isNumeric) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (numeric string is expected)',
|
||||
);
|
||||
}
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
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,2 +1 @@
|
||||
export * from './console-logger.service';
|
||||
export * from './logger.service';
|
||||
|
||||
@@ -1,264 +1,182 @@
|
||||
import { Injectable } from '../decorators/core/injectable.decorator';
|
||||
import { Optional } from '../decorators/core/optional.decorator';
|
||||
import { isObject } from '../utils/shared.utils';
|
||||
import { ConsoleLogger } from './console-logger.service';
|
||||
import { isLogLevelEnabled } from './utils';
|
||||
import { clc, yellow } from '../utils/cli-colors.util';
|
||||
import { isObject, isPlainObject } from '../utils/shared.utils';
|
||||
|
||||
declare const process: any;
|
||||
|
||||
export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose';
|
||||
|
||||
export interface LoggerService {
|
||||
/**
|
||||
* 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;
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
protected static logBuffer = new Array<LogBufferRecord>();
|
||||
protected static staticInstanceRef?: LoggerService = DEFAULT_LOGGER;
|
||||
protected static logLevels?: LogLevel[];
|
||||
private static isBufferAttached: boolean;
|
||||
private static logLevels: LogLevel[] = [
|
||||
'log',
|
||||
'error',
|
||||
'warn',
|
||||
'debug',
|
||||
'verbose',
|
||||
];
|
||||
private static lastTimestamp?: number;
|
||||
protected static instance?: typeof Logger | LoggerService = Logger;
|
||||
|
||||
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() protected options: { timestamp?: boolean } = {},
|
||||
@Optional() private readonly isTimestampEnabled = false,
|
||||
) {}
|
||||
|
||||
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;
|
||||
error(message: any, trace = '', context?: string) {
|
||||
const instance = this.getInstance();
|
||||
if (!this.isLogLevelEnabled('error')) {
|
||||
return;
|
||||
}
|
||||
return Logger.staticInstanceRef;
|
||||
instance &&
|
||||
instance.error.call(instance, message, trace, context || this.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);
|
||||
log(message: any, context?: string) {
|
||||
this.callFunction('log', 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);
|
||||
warn(message: any, context?: string) {
|
||||
this.callFunction('warn', 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);
|
||||
debug(message: any, context?: string) {
|
||||
this.callFunction('debug', 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);
|
||||
verbose(message: any, context?: string) {
|
||||
this.callFunction('verbose', message, 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);
|
||||
setContext(context: string) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
getTimestamp() {
|
||||
return Logger.getTimestamp();
|
||||
}
|
||||
|
||||
static overrideLogger(logger: LoggerService | LogLevel[] | boolean) {
|
||||
if (Array.isArray(logger)) {
|
||||
Logger.logLevels = logger;
|
||||
return this.staticInstanceRef?.setLogLevels(logger);
|
||||
this.logLevels = logger;
|
||||
return;
|
||||
}
|
||||
this.staticInstanceRef = isObject(logger)
|
||||
? (logger as LoggerService)
|
||||
: undefined;
|
||||
this.instance = isObject(logger) ? (logger as LoggerService) : undefined;
|
||||
}
|
||||
|
||||
static isLevelEnabled(level: LogLevel): boolean {
|
||||
const logLevels = Logger.logLevels;
|
||||
return isLogLevelEnabled(level, logLevels);
|
||||
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 timestamp = instance.getTimestamp
|
||||
? instance.getTimestamp()
|
||||
: Logger.getTimestamp?.();
|
||||
const computedMessage = `${pidMessage}${timestamp} ${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`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './is-log-level-enabled.util';
|
||||
@@ -1,32 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { Controller } from '../../decorators/core/controller.decorator';
|
||||
describe('@Controller', () => {
|
||||
const reflectedPath = 'test';
|
||||
const reflectedHost = 'api.example.com';
|
||||
const reflectedHostArray = ['api1.example.com', 'api2.example.com'];
|
||||
|
||||
@Controller(reflectedPath)
|
||||
class Test {}
|
||||
@@ -14,6 +15,9 @@ describe('@Controller', () => {
|
||||
@Controller({ path: reflectedPath, host: reflectedHost })
|
||||
class PathAndHostDecorator {}
|
||||
|
||||
@Controller({ path: reflectedPath, host: reflectedHostArray })
|
||||
class PathAndHostArrayDecorator {}
|
||||
|
||||
@Controller({ host: reflectedHost })
|
||||
class HostOnlyDecorator {}
|
||||
|
||||
@@ -29,6 +33,8 @@ describe('@Controller', () => {
|
||||
expect(host).to.be.eql(reflectedHost);
|
||||
const host2 = Reflect.getMetadata('host', HostOnlyDecorator);
|
||||
expect(host2).to.be.eql(reflectedHost);
|
||||
const host3 = Reflect.getMetadata('host', PathAndHostArrayDecorator);
|
||||
expect(host3).to.be.eql(reflectedHostArray);
|
||||
});
|
||||
|
||||
it('should set default path when no object passed as param', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('@Inject', () => {
|
||||
const metadata = Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, Test);
|
||||
|
||||
const expectedMetadata = [
|
||||
{ index: 2, param: opaqueToken },
|
||||
{ index: 2, param: opaqueToken.name },
|
||||
{ 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(`HttpException: ${message}`);
|
||||
expect(`${error}`).to.be.eql(`Error: ${message}`);
|
||||
});
|
||||
|
||||
describe('when "response" is an object', () => {
|
||||
@@ -71,10 +71,8 @@ describe('HttpException', () => {
|
||||
const error = new HttpException(obj, 400);
|
||||
const badRequestError = new BadRequestException(obj);
|
||||
|
||||
expect(`${error}`).to.be.eql(`HttpException: Http Exception`);
|
||||
expect(`${badRequestError}`).to.be.eql(
|
||||
`BadRequestException: Bad Request Exception`,
|
||||
);
|
||||
expect(`${error}`).to.be.eql(`Error: Http Exception`);
|
||||
expect(`${badRequestError}`).to.be.eql(`Error: Bad Request Exception`);
|
||||
expect(`${error}`.includes('[object Object]')).to.not.be.true;
|
||||
expect(`${badRequestError}`.includes('[object Object]')).to.not.be.true;
|
||||
});
|
||||
@@ -82,7 +80,7 @@ describe('HttpException', () => {
|
||||
it('should concat strings', () => {
|
||||
const test = 'test message';
|
||||
const error = new HttpException(test, 400);
|
||||
expect(`${error}`).to.be.eql(`HttpException: ${test}`);
|
||||
expect(`${error}`).to.be.eql(`Error: ${test}`);
|
||||
expect(`${error}`.includes('[object Object]')).to.not.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { Readable } from 'stream';
|
||||
import { StreamableFile } from '../../file-stream';
|
||||
|
||||
describe('StreamableFile', () => {
|
||||
describe('when input is a readable stream', () => {
|
||||
it('should assing it to a stream class property', () => {
|
||||
const stream = new Readable();
|
||||
const streamableFile = new StreamableFile(stream);
|
||||
expect(streamableFile.getStream()).to.equal(stream);
|
||||
});
|
||||
});
|
||||
describe('when input is an object with "pipe" method', () => {
|
||||
it('should assing it to a stream class property', () => {
|
||||
const stream = { pipe: () => {} };
|
||||
const streamableFile = new StreamableFile(stream as any);
|
||||
expect(streamableFile.getStream()).to.equal(stream);
|
||||
});
|
||||
});
|
||||
describe('otherwise', () => {
|
||||
it('should create a readable straem and push the input buffer', () => {
|
||||
const buffer = Buffer.from('test');
|
||||
const streamableFile = new StreamableFile(buffer);
|
||||
const stream = streamableFile.getStream();
|
||||
expect(stream.read()).to.equal(buffer);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { HttpException } from '../../exceptions';
|
||||
import { ArgumentMetadata } from '../../interfaces';
|
||||
import { ParseEnumPipe } from '../../pipes/parse-enum.pipe';
|
||||
|
||||
class CustomTestError extends HttpException {
|
||||
constructor() {
|
||||
super('This is a TestException', 418);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ParseEnumPipe', () => {
|
||||
enum Direction {
|
||||
Up = 'UP',
|
||||
}
|
||||
let target: ParseEnumPipe;
|
||||
beforeEach(() => {
|
||||
target = new ParseEnumPipe(Direction, {
|
||||
exceptionFactory: (error: any) => new CustomTestError(),
|
||||
});
|
||||
});
|
||||
describe('transform', () => {
|
||||
describe('when validation passes', () => {
|
||||
it('should return enum value', async () => {
|
||||
expect(await target.transform('UP', {} as ArgumentMetadata)).to.equal(
|
||||
Direction.Up,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
return expect(
|
||||
target.transform('DOWN', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(CustomTestError);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('constructor', () => {
|
||||
it('should throw an error if "enumType" is undefined/null', () => {
|
||||
try {
|
||||
new ParseEnumPipe(null);
|
||||
} catch (err) {
|
||||
expect(err.message).to.equal(
|
||||
`"ParseEnumPipe" requires "enumType" argument specified (to validate input values).`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { ArgumentMetadata } from '../../interfaces';
|
||||
import { ParseFloatPipe } from '../../pipes/parse-float.pipe';
|
||||
import { HttpException } from '../../exceptions';
|
||||
|
||||
class CustomTestError extends HttpException {
|
||||
constructor() {
|
||||
super('This is a TestException', 418);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ParseFloatPipe', () => {
|
||||
let target: ParseFloatPipe;
|
||||
beforeEach(() => {
|
||||
target = new ParseFloatPipe({
|
||||
exceptionFactory: (error: any) => new CustomTestError(),
|
||||
});
|
||||
});
|
||||
describe('transform', () => {
|
||||
describe('when validation passes', () => {
|
||||
it('should return number', async () => {
|
||||
const num = '3.33';
|
||||
expect(await target.transform(num, {} as ArgumentMetadata)).to.equal(
|
||||
parseFloat(num),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
return expect(
|
||||
target.transform('123.123abc', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(CustomTestError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,502 +0,0 @@
|
||||
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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
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,7 +9,6 @@ import {
|
||||
isPlainObject,
|
||||
isString,
|
||||
isUndefined,
|
||||
normalizePath,
|
||||
} from '../../utils/shared.utils';
|
||||
|
||||
function Foo(a) {
|
||||
@@ -18,34 +17,34 @@ function Foo(a) {
|
||||
|
||||
describe('Shared utils', () => {
|
||||
describe('isUndefined', () => {
|
||||
it('should return true when obj is undefined', () => {
|
||||
it('should returns true when obj is undefined', () => {
|
||||
expect(isUndefined(undefined)).to.be.true;
|
||||
});
|
||||
it('should return false when object is not undefined', () => {
|
||||
it('should returns false when object is not undefined', () => {
|
||||
expect(isUndefined({})).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('isFunction', () => {
|
||||
it('should return true when obj is function', () => {
|
||||
it('should returns true when obj is function', () => {
|
||||
expect(isFunction(() => ({}))).to.be.true;
|
||||
});
|
||||
it('should return false when object is not function', () => {
|
||||
it('should returns false when object is not function', () => {
|
||||
expect(isFunction(null)).to.be.false;
|
||||
expect(isFunction(undefined)).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('isObject', () => {
|
||||
it('should return true when obj is object', () => {
|
||||
it('should returns true when obj is object', () => {
|
||||
expect(isObject({})).to.be.true;
|
||||
});
|
||||
it('should return false when object is not object', () => {
|
||||
it('should returns 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 return true when obj is plain object', () => {
|
||||
it('should returns true when obj is plain object', () => {
|
||||
expect(isPlainObject({})).to.be.true;
|
||||
expect(isPlainObject({ prop: true })).to.be.true;
|
||||
expect(
|
||||
@@ -55,7 +54,7 @@ describe('Shared utils', () => {
|
||||
).to.be.true;
|
||||
expect(isPlainObject(Object.create(null))).to.be.true;
|
||||
});
|
||||
it('should return false when object is not object', () => {
|
||||
it('should returns false when object is not object', () => {
|
||||
expect(isPlainObject(3)).to.be.false;
|
||||
expect(isPlainObject(null)).to.be.false;
|
||||
expect(isPlainObject(undefined)).to.be.false;
|
||||
@@ -65,69 +64,52 @@ describe('Shared utils', () => {
|
||||
});
|
||||
});
|
||||
describe('isString', () => {
|
||||
it('should return true when obj is a string', () => {
|
||||
it('should returns true when obj is string', () => {
|
||||
expect(isString('true')).to.be.true;
|
||||
});
|
||||
it('should return false when object is not a string', () => {
|
||||
it('should returns false when object is not string', () => {
|
||||
expect(isString(false)).to.be.false;
|
||||
expect(isString(null)).to.be.false;
|
||||
expect(isString(undefined)).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('isConstructor', () => {
|
||||
it('should return true when string is equal to constructor', () => {
|
||||
it('should returns true when string is equal constructor', () => {
|
||||
expect(isConstructor('constructor')).to.be.true;
|
||||
});
|
||||
it('should return false when string is not equal to constructor', () => {
|
||||
it('should returns false when string is not equal constructor', () => {
|
||||
expect(isConstructor('nope')).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('addLeadingSlash', () => {
|
||||
it('should return the validated path ("add / if not exists")', () => {
|
||||
it('should returns validated path ("add / if not exists")', () => {
|
||||
expect(addLeadingSlash('nope')).to.be.eql('/nope');
|
||||
});
|
||||
it('should return the same path', () => {
|
||||
it('should returns same path', () => {
|
||||
expect(addLeadingSlash('/nope')).to.be.eql('/nope');
|
||||
});
|
||||
it('should return empty path', () => {
|
||||
it('should returns 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 return true when obj is undefined or null', () => {
|
||||
it('should returns true when obj is undefined or null', () => {
|
||||
expect(isNil(undefined)).to.be.true;
|
||||
expect(isNil(null)).to.be.true;
|
||||
});
|
||||
it('should return false when object is not undefined and null', () => {
|
||||
it('should returns false when object is not undefined and null', () => {
|
||||
expect(isNil('3')).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('isEmpty', () => {
|
||||
it('should return true when array is empty or not exists', () => {
|
||||
it('should returns 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 return false when array is not empty', () => {
|
||||
it('should returns false when array is not empty', () => {
|
||||
expect(isEmpty([1, 2])).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,13 +33,6 @@ 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,6 +17,14 @@ 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() {}
|
||||
@@ -61,12 +69,6 @@ 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,4 +1,3 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { ExceptionHandler } from './exception-handler';
|
||||
|
||||
const DEFAULT_TEARDOWN = () => process.exit(1);
|
||||
@@ -9,15 +8,11 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -25,15 +20,11 @@ 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,15 +46,10 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
|
||||
host: ArgumentsHost,
|
||||
applicationRef: AbstractHttpAdapter | HttpServer,
|
||||
) {
|
||||
const body = this.isHttpError(exception)
|
||||
? {
|
||||
statusCode: exception.statusCode,
|
||||
message: exception.message,
|
||||
}
|
||||
: {
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
|
||||
};
|
||||
const body = {
|
||||
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(
|
||||
@@ -68,12 +63,4 @@ 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, Type } from '@nestjs/common/interfaces';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
@@ -54,17 +54,15 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
}
|
||||
|
||||
public getGuardInstance(
|
||||
metatype: Function | CanActivate,
|
||||
guard: Function | CanActivate,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): CanActivate | null {
|
||||
const isObject = (metatype as CanActivate).canActivate;
|
||||
const isObject = (guard as CanActivate).canActivate;
|
||||
if (isObject) {
|
||||
return metatype as CanActivate;
|
||||
return guard as CanActivate;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(
|
||||
metatype as Type<unknown>,
|
||||
);
|
||||
const instanceWrapper = this.getInstanceByMetatype(guard);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
@@ -75,8 +73,8 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype(
|
||||
metatype: Type<unknown>,
|
||||
public getInstanceByMetatype<T extends Record<string, any> | Function>(
|
||||
guard: T,
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return;
|
||||
@@ -87,7 +85,7 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
return;
|
||||
}
|
||||
const injectables = moduleRef.injectables;
|
||||
return injectables.get(metatype);
|
||||
return injectables.get(guard.name as string);
|
||||
}
|
||||
|
||||
public getGlobalMetadata<T extends unknown[]>(
|
||||
|
||||
@@ -13,6 +13,7 @@ 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';
|
||||
@@ -104,7 +105,7 @@ export class ExternalContextCreator {
|
||||
},
|
||||
contextType: TContext = 'http' as TContext,
|
||||
) {
|
||||
const module = this.getContextModuleKey(instance.constructor);
|
||||
const module = this.getContextModuleName(instance.constructor);
|
||||
const { argsLength, paramtypes, getParamsMetadata } = this.getMetadata<
|
||||
TParamsMetadata,
|
||||
TContext
|
||||
@@ -237,18 +238,26 @@ export class ExternalContextCreator {
|
||||
return handlerMetadata;
|
||||
}
|
||||
|
||||
public getContextModuleKey(moduleCtor: Function | undefined): string {
|
||||
const emptyModuleKey = '';
|
||||
if (!moduleCtor) {
|
||||
return emptyModuleKey;
|
||||
public getContextModuleName(constructor: Function): string {
|
||||
const defaultModuleName = '';
|
||||
const className = constructor.name;
|
||||
if (!className) {
|
||||
return defaultModuleName;
|
||||
}
|
||||
const moduleContainerEntries = this.modulesContainer.entries();
|
||||
for (const [key, moduleRef] of moduleContainerEntries) {
|
||||
if (moduleRef.hasProvider(moduleCtor)) {
|
||||
for (const [key, module] of [...this.modulesContainer.entries()]) {
|
||||
if (this.getProviderByClassName(module, className)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return emptyModuleKey;
|
||||
return defaultModuleName;
|
||||
}
|
||||
|
||||
public getProviderByClassName(module: Module, className: string): boolean {
|
||||
const { providers } = module;
|
||||
const hasProvider = [...providers.keys()].some(
|
||||
provider => provider === className,
|
||||
);
|
||||
return hasProvider;
|
||||
}
|
||||
|
||||
public exchangeKeysForValues<TMetadata = any>(
|
||||
|
||||
@@ -7,7 +7,7 @@ export class RouterMethodFactory {
|
||||
case RequestMethod.POST:
|
||||
return target.post;
|
||||
case RequestMethod.ALL:
|
||||
return target.all;
|
||||
return target.use;
|
||||
case RequestMethod.DELETE:
|
||||
return target.delete;
|
||||
case RequestMethod.PUT:
|
||||
@@ -18,10 +18,8 @@ export class RouterMethodFactory {
|
||||
return target.options;
|
||||
case RequestMethod.HEAD:
|
||||
return target.head;
|
||||
case RequestMethod.GET:
|
||||
return target.get;
|
||||
default: {
|
||||
return target.use;
|
||||
return target.get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class NestContainer {
|
||||
scope: Type<any>[],
|
||||
): Promise<Module> {
|
||||
// In DependenciesScanner#scanForModules we already check for undefined or invalid modules
|
||||
// We still need to catch the edge-case of `forwardRef(() => undefined)`
|
||||
// We sill need to catch the edge-case of `forwardRef(() => undefined)`
|
||||
if (!metatype) {
|
||||
throw new UndefinedForwardRefException(scope);
|
||||
}
|
||||
@@ -143,10 +143,7 @@ export class NestContainer {
|
||||
moduleRef.addRelatedModule(related);
|
||||
}
|
||||
|
||||
public addProvider(
|
||||
provider: Provider,
|
||||
token: string,
|
||||
): string | symbol | Function {
|
||||
public addProvider(provider: Provider, token: string): string {
|
||||
if (!provider) {
|
||||
throw new CircularDependencyException();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
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: [InstanceToken, InstanceWrapper][],
|
||||
instances: [string, InstanceWrapper][],
|
||||
): InstanceWrapper[] {
|
||||
return iterate(instances)
|
||||
.filter(([_, wrapper]) => wrapper.isDependencyTreeStatic())
|
||||
@@ -23,7 +22,7 @@ export function getTransientInstances(
|
||||
* @param instances The instances which should be checked whether they are transcient
|
||||
*/
|
||||
export function getNonTransientInstances(
|
||||
instances: [InstanceToken, InstanceWrapper][],
|
||||
instances: [string, InstanceWrapper][],
|
||||
): InstanceWrapper[] {
|
||||
return iterate(instances)
|
||||
.filter(
|
||||
|
||||
@@ -9,6 +9,7 @@ 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,
|
||||
@@ -26,7 +27,7 @@ import {
|
||||
InstanceWrapper,
|
||||
PropertyMetadata,
|
||||
} from './instance-wrapper';
|
||||
import { InstanceToken, Module } from './module';
|
||||
import { Module } from './module';
|
||||
|
||||
/**
|
||||
* The type of an injectable dependency
|
||||
@@ -53,9 +54,9 @@ export interface InjectorDependencyContext {
|
||||
*/
|
||||
key?: string | symbol;
|
||||
/**
|
||||
* The function itself, the name of the function, or injection token.
|
||||
* The name of the function or injection token
|
||||
*/
|
||||
name?: Function | string | symbol;
|
||||
name?: string | symbol;
|
||||
/**
|
||||
* The index of the dependency which gets injected
|
||||
* from the dependencies array
|
||||
@@ -69,27 +70,27 @@ export interface InjectorDependencyContext {
|
||||
|
||||
export class Injector {
|
||||
public loadPrototype<T>(
|
||||
{ token }: InstanceWrapper<T>,
|
||||
collection: Map<InstanceToken, InstanceWrapper<T>>,
|
||||
{ name }: InstanceWrapper<T>,
|
||||
collection: Map<string, InstanceWrapper<T>>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
const target = collection.get(token);
|
||||
const target = collection.get(name);
|
||||
const instance = target.createPrototype(contextId);
|
||||
if (instance) {
|
||||
const wrapper = new InstanceWrapper({
|
||||
...target,
|
||||
instance,
|
||||
});
|
||||
collection.set(token, wrapper);
|
||||
collection.set(name, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
public async loadInstance<T>(
|
||||
wrapper: InstanceWrapper<T>,
|
||||
collection: Map<InstanceToken, InstanceWrapper>,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
moduleRef: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
@@ -100,10 +101,9 @@ export class Injector {
|
||||
return instanceHost.donePromise;
|
||||
}
|
||||
const done = this.applyDoneHook(instanceHost);
|
||||
const token = wrapper.token || wrapper.name;
|
||||
const { name, inject } = wrapper;
|
||||
|
||||
const { inject } = wrapper;
|
||||
const targetWrapper = collection.get(token);
|
||||
const targetWrapper = collection.get(name);
|
||||
if (isUndefined(targetWrapper)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
@@ -142,13 +142,13 @@ export class Injector {
|
||||
|
||||
public async loadMiddleware(
|
||||
wrapper: InstanceWrapper,
|
||||
collection: Map<InstanceToken, InstanceWrapper>,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
moduleRef: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const { metatype, token } = wrapper;
|
||||
const targetWrapper = collection.get(token);
|
||||
const { metatype } = wrapper;
|
||||
const targetWrapper = collection.get(metatype.name);
|
||||
if (!isUndefined(targetWrapper.instance)) {
|
||||
return;
|
||||
}
|
||||
@@ -317,7 +317,7 @@ export class Injector {
|
||||
const token = this.resolveParamToken(wrapper, param);
|
||||
return this.resolveComponentInstance<T>(
|
||||
moduleRef,
|
||||
token,
|
||||
isFunction(token) ? (token as Type<any>).name : token,
|
||||
dependencyContext,
|
||||
wrapper,
|
||||
contextId,
|
||||
@@ -339,7 +339,7 @@ export class Injector {
|
||||
|
||||
public async resolveComponentInstance<T>(
|
||||
moduleRef: Module,
|
||||
token: InstanceToken,
|
||||
name: any,
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
@@ -350,7 +350,7 @@ export class Injector {
|
||||
const instanceWrapper = await this.lookupComponent(
|
||||
providers,
|
||||
moduleRef,
|
||||
{ ...dependencyContext, name: token },
|
||||
{ ...dependencyContext, name },
|
||||
wrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
@@ -407,7 +407,7 @@ export class Injector {
|
||||
}
|
||||
|
||||
public async lookupComponent<T = any>(
|
||||
providers: Map<Function | string | symbol, InstanceWrapper>,
|
||||
providers: Map<string | symbol, InstanceWrapper>,
|
||||
moduleRef: Module,
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
@@ -467,7 +467,7 @@ export class Injector {
|
||||
|
||||
public async lookupComponentInImports(
|
||||
moduleRef: Module,
|
||||
name: InstanceToken,
|
||||
name: any,
|
||||
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),
|
||||
contextModuleExports.has(child.metatype && child.metatype.name),
|
||||
);
|
||||
}
|
||||
for (const relatedModule of children) {
|
||||
@@ -552,7 +552,7 @@ export class Injector {
|
||||
try {
|
||||
const dependencyContext = {
|
||||
key: item.key,
|
||||
name: item.name as Function | string | symbol,
|
||||
name: item.name as string,
|
||||
};
|
||||
if (this.isInquirer(item.name, parentInquirer)) {
|
||||
return parentInquirer && parentInquirer.instance;
|
||||
@@ -656,12 +656,15 @@ export class Injector {
|
||||
public async loadPerContext<T = any>(
|
||||
instance: T,
|
||||
moduleRef: Module,
|
||||
collection: Map<InstanceToken, InstanceWrapper>,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
ctx: ContextId,
|
||||
wrapper?: InstanceWrapper,
|
||||
): Promise<T> {
|
||||
if (!wrapper) {
|
||||
const injectionToken = instance.constructor;
|
||||
const providerCtor = instance.constructor;
|
||||
const injectionToken =
|
||||
(providerCtor && providerCtor.name) ||
|
||||
((providerCtor as unknown) as string);
|
||||
wrapper = collection.get(injectionToken);
|
||||
}
|
||||
await this.loadInstance(wrapper, collection, moduleRef, ctx, wrapper);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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 { InstanceToken, Module } from './module';
|
||||
import { Module } from './module';
|
||||
|
||||
type InstanceToken = string | symbol | Type<any> | Abstract<any> | Function;
|
||||
type HostCollection = 'providers' | 'controllers' | 'injectables';
|
||||
|
||||
export interface InstanceLink<T = any> {
|
||||
@@ -21,17 +23,20 @@ export class InstanceLinksHost {
|
||||
}
|
||||
|
||||
get<T = any>(token: InstanceToken, moduleId?: string): InstanceLink<T> {
|
||||
const modulesMap = this.instanceLinks.get(token);
|
||||
const name = isFunction(token)
|
||||
? (token as Function).name
|
||||
: (token as string | symbol);
|
||||
const modulesMap = this.instanceLinks.get(name);
|
||||
|
||||
if (!modulesMap) {
|
||||
throw new UnknownElementException(this.getInstanceNameByToken(token));
|
||||
throw new UnknownElementException(name);
|
||||
}
|
||||
const instanceLink = moduleId
|
||||
? modulesMap.find(item => item.moduleId === moduleId)
|
||||
: modulesMap[modulesMap.length - 1];
|
||||
|
||||
if (!instanceLink) {
|
||||
throw new UnknownElementException(this.getInstanceNameByToken(token));
|
||||
throw new UnknownElementException(name);
|
||||
}
|
||||
return instanceLink;
|
||||
}
|
||||
@@ -71,8 +76,4 @@ export class InstanceLinksHost {
|
||||
existingLinks.push(instanceLink);
|
||||
}
|
||||
}
|
||||
|
||||
private getInstanceNameByToken(token: InstanceToken): string {
|
||||
return isFunction(token) ? (token as Function)?.name : (token as string);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ import { Module } from './module';
|
||||
|
||||
export class InstanceLoader {
|
||||
private readonly injector = new Injector();
|
||||
private readonly logger = new Logger(InstanceLoader.name, {
|
||||
timestamp: true,
|
||||
});
|
||||
private readonly logger = new Logger(InstanceLoader.name, true);
|
||||
|
||||
constructor(private readonly container: NestContainer) {}
|
||||
|
||||
@@ -23,69 +21,69 @@ export class InstanceLoader {
|
||||
}
|
||||
|
||||
private createPrototypes(modules: Map<string, Module>) {
|
||||
modules.forEach(moduleRef => {
|
||||
this.createPrototypesOfProviders(moduleRef);
|
||||
this.createPrototypesOfInjectables(moduleRef);
|
||||
this.createPrototypesOfControllers(moduleRef);
|
||||
modules.forEach(module => {
|
||||
this.createPrototypesOfProviders(module);
|
||||
this.createPrototypesOfInjectables(module);
|
||||
this.createPrototypesOfControllers(module);
|
||||
});
|
||||
}
|
||||
|
||||
private async createInstances(modules: Map<string, Module>) {
|
||||
await Promise.all(
|
||||
[...modules.values()].map(async moduleRef => {
|
||||
await this.createInstancesOfProviders(moduleRef);
|
||||
await this.createInstancesOfInjectables(moduleRef);
|
||||
await this.createInstancesOfControllers(moduleRef);
|
||||
[...modules.values()].map(async module => {
|
||||
await this.createInstancesOfProviders(module);
|
||||
await this.createInstancesOfInjectables(module);
|
||||
await this.createInstancesOfControllers(module);
|
||||
|
||||
const { name } = moduleRef.metatype;
|
||||
const { name } = module.metatype;
|
||||
this.isModuleWhitelisted(name) &&
|
||||
this.logger.log(MODULE_INIT_MESSAGE`${name}`);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private createPrototypesOfProviders(moduleRef: Module) {
|
||||
const { providers } = moduleRef;
|
||||
private createPrototypesOfProviders(module: Module) {
|
||||
const { providers } = module;
|
||||
providers.forEach(wrapper =>
|
||||
this.injector.loadPrototype<Injectable>(wrapper, providers),
|
||||
);
|
||||
}
|
||||
|
||||
private async createInstancesOfProviders(moduleRef: Module) {
|
||||
const { providers } = moduleRef;
|
||||
private async createInstancesOfProviders(module: Module) {
|
||||
const { providers } = module;
|
||||
const wrappers = [...providers.values()];
|
||||
await Promise.all(
|
||||
wrappers.map(item => this.injector.loadProvider(item, moduleRef)),
|
||||
wrappers.map(item => this.injector.loadProvider(item, module)),
|
||||
);
|
||||
}
|
||||
|
||||
private createPrototypesOfControllers(moduleRef: Module) {
|
||||
const { controllers } = moduleRef;
|
||||
private createPrototypesOfControllers(module: Module) {
|
||||
const { controllers } = module;
|
||||
controllers.forEach(wrapper =>
|
||||
this.injector.loadPrototype<Controller>(wrapper, controllers),
|
||||
);
|
||||
}
|
||||
|
||||
private async createInstancesOfControllers(moduleRef: Module) {
|
||||
const { controllers } = moduleRef;
|
||||
private async createInstancesOfControllers(module: Module) {
|
||||
const { controllers } = module;
|
||||
const wrappers = [...controllers.values()];
|
||||
await Promise.all(
|
||||
wrappers.map(item => this.injector.loadController(item, moduleRef)),
|
||||
wrappers.map(item => this.injector.loadController(item, module)),
|
||||
);
|
||||
}
|
||||
|
||||
private createPrototypesOfInjectables(moduleRef: Module) {
|
||||
const { injectables } = moduleRef;
|
||||
private createPrototypesOfInjectables(module: Module) {
|
||||
const { injectables } = module;
|
||||
injectables.forEach(wrapper =>
|
||||
this.injector.loadPrototype(wrapper, injectables),
|
||||
);
|
||||
}
|
||||
|
||||
private async createInstancesOfInjectables(moduleRef: Module) {
|
||||
const { injectables } = moduleRef;
|
||||
private async createInstancesOfInjectables(module: Module) {
|
||||
const { injectables } = module;
|
||||
const wrappers = [...injectables.values()];
|
||||
await Promise.all(
|
||||
wrappers.map(item => this.injector.loadInjectable(item, moduleRef)),
|
||||
wrappers.map(item => this.injector.loadInjectable(item, module)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { InstanceToken, Module } from './module';
|
||||
import { Module } from './module';
|
||||
|
||||
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
|
||||
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
|
||||
@@ -36,7 +36,6 @@ 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,25 +4,10 @@ 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,
|
||||
ReflectorAliasProvider,
|
||||
requestProvider,
|
||||
inquirerProvider,
|
||||
],
|
||||
exports: [
|
||||
Reflector,
|
||||
ReflectorAliasProvider,
|
||||
requestProvider,
|
||||
inquirerProvider,
|
||||
],
|
||||
providers: [Reflector, requestProvider, inquirerProvider],
|
||||
exports: [Reflector, requestProvider, inquirerProvider],
|
||||
})
|
||||
export class InternalCoreModule {
|
||||
static register(providers: ValueProvider[]): DynamicModule {
|
||||
|
||||
@@ -31,33 +31,21 @@ import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { ModuleRef } from './module-ref';
|
||||
|
||||
export type InstanceToken =
|
||||
| string
|
||||
| symbol
|
||||
| Type<any>
|
||||
| Abstract<any>
|
||||
| Function;
|
||||
interface ProviderName {
|
||||
name?: string | symbol;
|
||||
}
|
||||
|
||||
export class Module {
|
||||
private readonly _id: string;
|
||||
private readonly _imports = new Set<Module>();
|
||||
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 _providers = new Map<any, InstanceWrapper<Injectable>>();
|
||||
private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>();
|
||||
private readonly _middlewares = new Map<any, InstanceWrapper<Injectable>>();
|
||||
private readonly _controllers = new Map<
|
||||
InstanceToken,
|
||||
string,
|
||||
InstanceWrapper<Controller>
|
||||
>();
|
||||
private readonly _exports = new Set<InstanceToken>();
|
||||
private readonly _exports = new Set<string | symbol>();
|
||||
private _distance = 0;
|
||||
|
||||
constructor(
|
||||
@@ -72,11 +60,11 @@ export class Module {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get providers(): Map<InstanceToken, InstanceWrapper<Injectable>> {
|
||||
get providers(): Map<any, InstanceWrapper<Injectable>> {
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
get middlewares(): Map<InstanceToken, InstanceWrapper<Injectable>> {
|
||||
get middlewares(): Map<any, InstanceWrapper<Injectable>> {
|
||||
return this._middlewares;
|
||||
}
|
||||
|
||||
@@ -94,34 +82,34 @@ export class Module {
|
||||
/**
|
||||
* Left for backward-compatibility reasons
|
||||
*/
|
||||
get components(): Map<InstanceToken, InstanceWrapper<Injectable>> {
|
||||
get components(): Map<string, InstanceWrapper<Injectable>> {
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left for backward-compatibility reasons
|
||||
*/
|
||||
get routes(): Map<InstanceToken, InstanceWrapper<Controller>> {
|
||||
get routes(): Map<string, InstanceWrapper<Controller>> {
|
||||
return this._controllers;
|
||||
}
|
||||
|
||||
get injectables(): Map<InstanceToken, InstanceWrapper<Injectable>> {
|
||||
get injectables(): Map<string, InstanceWrapper<Injectable>> {
|
||||
return this._injectables;
|
||||
}
|
||||
|
||||
get controllers(): Map<InstanceToken, InstanceWrapper<Controller>> {
|
||||
get controllers(): Map<string, InstanceWrapper<Controller>> {
|
||||
return this._controllers;
|
||||
}
|
||||
|
||||
get exports(): Set<InstanceToken> {
|
||||
get exports(): Set<string | symbol> {
|
||||
return this._exports;
|
||||
}
|
||||
|
||||
get instance(): NestModule {
|
||||
if (!this._providers.has(this._metatype)) {
|
||||
if (!this._providers.has(this._metatype.name)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
const module = this._providers.get(this._metatype);
|
||||
const module = this._providers.get(this._metatype.name);
|
||||
return module.instance as NestModule;
|
||||
}
|
||||
|
||||
@@ -146,9 +134,8 @@ export class Module {
|
||||
public addModuleRef() {
|
||||
const moduleRef = this.createModuleReferenceType();
|
||||
this._providers.set(
|
||||
ModuleRef,
|
||||
ModuleRef.name,
|
||||
new InstanceWrapper({
|
||||
token: ModuleRef,
|
||||
name: ModuleRef.name,
|
||||
metatype: ModuleRef as any,
|
||||
isResolved: true,
|
||||
@@ -160,9 +147,8 @@ export class Module {
|
||||
|
||||
public addModuleAsProvider() {
|
||||
this._providers.set(
|
||||
this._metatype,
|
||||
this._metatype.name,
|
||||
new InstanceWrapper({
|
||||
token: this._metatype,
|
||||
name: this._metatype.name,
|
||||
metatype: this._metatype,
|
||||
isResolved: false,
|
||||
@@ -174,9 +160,8 @@ export class Module {
|
||||
|
||||
public addApplicationConfig() {
|
||||
this._providers.set(
|
||||
ApplicationConfig,
|
||||
ApplicationConfig.name,
|
||||
new InstanceWrapper({
|
||||
token: ApplicationConfig,
|
||||
name: ApplicationConfig.name,
|
||||
isResolved: true,
|
||||
instance: this.container.applicationConfig,
|
||||
@@ -192,10 +177,9 @@ export class Module {
|
||||
if (this.isCustomProvider(injectable)) {
|
||||
return this.addCustomProvider(injectable, this._injectables);
|
||||
}
|
||||
let instanceWrapper = this.injectables.get(injectable);
|
||||
let instanceWrapper = this.injectables.get(injectable.name);
|
||||
if (!instanceWrapper) {
|
||||
instanceWrapper = new InstanceWrapper({
|
||||
token: injectable,
|
||||
name: injectable.name,
|
||||
metatype: injectable,
|
||||
instance: null,
|
||||
@@ -203,23 +187,23 @@ export class Module {
|
||||
scope: getClassScope(injectable),
|
||||
host: this,
|
||||
});
|
||||
this._injectables.set(injectable, instanceWrapper);
|
||||
this._injectables.set(injectable.name, instanceWrapper);
|
||||
}
|
||||
if (host) {
|
||||
const token = host && host.name;
|
||||
const hostWrapper =
|
||||
this._controllers.get(host) || this._providers.get(host);
|
||||
this._controllers.get(host && host.name) || this._providers.get(token);
|
||||
hostWrapper && hostWrapper.addEnhancerMetadata(instanceWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
public addProvider(provider: Provider) {
|
||||
public addProvider(provider: Provider): string {
|
||||
if (this.isCustomProvider(provider)) {
|
||||
return this.addCustomProvider(provider, this._providers);
|
||||
}
|
||||
this._providers.set(
|
||||
provider,
|
||||
(provider as Type<Injectable>).name,
|
||||
new InstanceWrapper({
|
||||
token: provider,
|
||||
name: (provider as Type<Injectable>).name,
|
||||
metatype: provider as Type<Injectable>,
|
||||
instance: null,
|
||||
@@ -228,7 +212,7 @@ export class Module {
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
return provider as Type<Injectable>;
|
||||
return (provider as Type<Injectable>).name;
|
||||
}
|
||||
|
||||
public isCustomProvider(
|
||||
@@ -248,13 +232,20 @@ export class Module {
|
||||
}
|
||||
|
||||
public addCustomProvider(
|
||||
provider:
|
||||
provider: (
|
||||
| ClassProvider
|
||||
| FactoryProvider
|
||||
| ValueProvider
|
||||
| ExistingProvider,
|
||||
collection: Map<Function | string | symbol, any>,
|
||||
) {
|
||||
| ExistingProvider
|
||||
) &
|
||||
ProviderName,
|
||||
collection: Map<string, any>,
|
||||
): string {
|
||||
const name = this.getProviderStaticToken(provider.provide) as string;
|
||||
provider = {
|
||||
...provider,
|
||||
name,
|
||||
};
|
||||
if (this.isCustomClass(provider)) {
|
||||
this.addCustomClass(provider, collection);
|
||||
} else if (this.isCustomValue(provider)) {
|
||||
@@ -264,7 +255,7 @@ export class Module {
|
||||
} else if (this.isCustomUseExisting(provider)) {
|
||||
this.addCustomUseExisting(provider, collection);
|
||||
}
|
||||
return provider.provide;
|
||||
return name;
|
||||
}
|
||||
|
||||
public isCustomClass(provider: any): provider is ClassProvider {
|
||||
@@ -288,20 +279,19 @@ export class Module {
|
||||
}
|
||||
|
||||
public addCustomClass(
|
||||
provider: ClassProvider,
|
||||
collection: Map<InstanceToken, InstanceWrapper>,
|
||||
provider: ClassProvider & ProviderName,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
let { scope } = provider;
|
||||
const { name, useClass } = provider;
|
||||
|
||||
const { useClass } = provider;
|
||||
let { scope } = provider;
|
||||
if (isUndefined(scope)) {
|
||||
scope = getClassScope(useClass);
|
||||
}
|
||||
collection.set(
|
||||
provider.provide,
|
||||
name as string,
|
||||
new InstanceWrapper({
|
||||
token: provider.provide,
|
||||
name: useClass?.name || useClass,
|
||||
name,
|
||||
metatype: useClass,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
@@ -312,15 +302,14 @@ export class Module {
|
||||
}
|
||||
|
||||
public addCustomValue(
|
||||
provider: ValueProvider,
|
||||
collection: Map<Function | string | symbol, InstanceWrapper>,
|
||||
provider: ValueProvider & ProviderName,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const { useValue: value, provide: providerToken } = provider;
|
||||
const { name, useValue: value } = provider;
|
||||
collection.set(
|
||||
providerToken,
|
||||
name as string,
|
||||
new InstanceWrapper({
|
||||
token: providerToken,
|
||||
name: (providerToken as Function)?.name || providerToken,
|
||||
name,
|
||||
metatype: null,
|
||||
instance: value,
|
||||
isResolved: true,
|
||||
@@ -331,21 +320,14 @@ export class Module {
|
||||
}
|
||||
|
||||
public addCustomFactory(
|
||||
provider: FactoryProvider,
|
||||
collection: Map<Function | string | symbol, InstanceWrapper>,
|
||||
provider: FactoryProvider & ProviderName,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const {
|
||||
useFactory: factory,
|
||||
inject,
|
||||
scope,
|
||||
provide: providerToken,
|
||||
} = provider;
|
||||
|
||||
const { name, useFactory: factory, inject, scope } = provider;
|
||||
collection.set(
|
||||
providerToken,
|
||||
name as string,
|
||||
new InstanceWrapper({
|
||||
token: providerToken,
|
||||
name: (providerToken as Function)?.name || providerToken,
|
||||
name,
|
||||
metatype: factory as any,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
@@ -357,15 +339,14 @@ export class Module {
|
||||
}
|
||||
|
||||
public addCustomUseExisting(
|
||||
provider: ExistingProvider,
|
||||
collection: Map<Function | string | symbol, InstanceWrapper>,
|
||||
provider: ExistingProvider & ProviderName,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
) {
|
||||
const { useExisting, provide: providerToken } = provider;
|
||||
const { name, useExisting } = provider;
|
||||
collection.set(
|
||||
providerToken,
|
||||
name as string,
|
||||
new InstanceWrapper({
|
||||
token: providerToken,
|
||||
name: (providerToken as Function)?.name || providerToken,
|
||||
name,
|
||||
metatype: (instance => instance) as any,
|
||||
instance: null,
|
||||
isResolved: false,
|
||||
@@ -377,9 +358,9 @@ export class Module {
|
||||
}
|
||||
|
||||
public addExportedProvider(
|
||||
provider: Provider | string | symbol | DynamicModule,
|
||||
provider: (Provider & ProviderName) | string | symbol | DynamicModule,
|
||||
) {
|
||||
const addExportedUnit = (token: InstanceToken) =>
|
||||
const addExportedUnit = (token: string | symbol) =>
|
||||
this._exports.add(this.validateExportedProvider(token));
|
||||
|
||||
if (this.isCustomProvider(provider as any)) {
|
||||
@@ -390,7 +371,7 @@ export class Module {
|
||||
const { module } = provider;
|
||||
return addExportedUnit(module.name);
|
||||
}
|
||||
addExportedUnit(provider as Type<any>);
|
||||
addExportedUnit(provider.name);
|
||||
}
|
||||
|
||||
public addCustomExportedProvider(
|
||||
@@ -404,10 +385,10 @@ export class Module {
|
||||
if (isString(provide) || isSymbol(provide)) {
|
||||
return this._exports.add(this.validateExportedProvider(provide));
|
||||
}
|
||||
this._exports.add(this.validateExportedProvider(provide));
|
||||
this._exports.add(this.validateExportedProvider(provide.name));
|
||||
}
|
||||
|
||||
public validateExportedProvider(token: InstanceToken) {
|
||||
public validateExportedProvider(token: string | symbol) {
|
||||
if (this._providers.has(token)) {
|
||||
return token;
|
||||
}
|
||||
@@ -421,17 +402,15 @@ export class Module {
|
||||
|
||||
if (!importsNames.includes(token as string)) {
|
||||
const { name } = this.metatype;
|
||||
const providerName = isFunction(token) ? (token as Function).name : token;
|
||||
throw new UnknownExportException(providerName as string, name);
|
||||
throw new UnknownExportException(token, name);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public addController(controller: Type<Controller>) {
|
||||
this._controllers.set(
|
||||
controller,
|
||||
controller.name,
|
||||
new InstanceWrapper({
|
||||
token: controller,
|
||||
name: controller.name,
|
||||
metatype: controller,
|
||||
instance: null,
|
||||
@@ -457,13 +436,15 @@ export class Module {
|
||||
this._imports.add(module);
|
||||
}
|
||||
|
||||
public replace(toReplace: InstanceToken, options: any) {
|
||||
public replace(toReplace: string | symbol | Type<any>, options: any) {
|
||||
if (options.isProvider && this.hasProvider(toReplace)) {
|
||||
const originalProvider = this._providers.get(toReplace);
|
||||
const name = this.getProviderStaticToken(toReplace);
|
||||
const originalProvider = this._providers.get(name);
|
||||
|
||||
return originalProvider.mergeWith({ provide: toReplace, ...options });
|
||||
} else if (!options.isProvider && this.hasInjectable(toReplace)) {
|
||||
const originalInjectable = this._injectables.get(toReplace);
|
||||
const name = this.getProviderStaticToken(toReplace);
|
||||
const originalInjectable = this._injectables.get(name);
|
||||
|
||||
return originalInjectable.mergeWith({
|
||||
provide: toReplace,
|
||||
@@ -472,21 +453,29 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
public hasProvider(token: InstanceToken): boolean {
|
||||
return this._providers.has(token);
|
||||
public hasProvider(token: string | symbol | Type<any>): boolean {
|
||||
const name = this.getProviderStaticToken(token);
|
||||
return this._providers.has(name);
|
||||
}
|
||||
|
||||
public hasInjectable(token: InstanceToken): boolean {
|
||||
return this._injectables.has(token);
|
||||
public hasInjectable(token: string | symbol | Type<any>): boolean {
|
||||
const name = this.getProviderStaticToken(token);
|
||||
return this._injectables.has(name);
|
||||
}
|
||||
|
||||
public getProviderByKey<T = any>(name: InstanceToken): InstanceWrapper<T> {
|
||||
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> {
|
||||
return this._providers.get(name) as InstanceWrapper<T>;
|
||||
}
|
||||
|
||||
public getNonAliasProviders(): Array<
|
||||
[InstanceToken, InstanceWrapper<Injectable>]
|
||||
> {
|
||||
public getNonAliasProviders(): Array<[string, InstanceWrapper<Injectable>]> {
|
||||
return [...this._providers].filter(([_, wrapper]) => !wrapper.isAlias);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { Module } from './module';
|
||||
|
||||
export class ModulesContainer extends Map<string, Module> {
|
||||
private readonly _applicationId = uuid();
|
||||
|
||||
get applicationId(): string {
|
||||
return this._applicationId;
|
||||
}
|
||||
}
|
||||
export class ModulesContainer extends Map<string, Module> {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { INTERCEPTORS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller, NestInterceptor, Type } from '@nestjs/common/interfaces';
|
||||
import { Controller, NestInterceptor } from '@nestjs/common/interfaces';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
@@ -59,17 +59,15 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
}
|
||||
|
||||
public getInterceptorInstance(
|
||||
metatype: Function | NestInterceptor,
|
||||
interceptor: Function | NestInterceptor,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): NestInterceptor | null {
|
||||
const isObject = (metatype as NestInterceptor).intercept;
|
||||
const isObject = (interceptor as NestInterceptor).intercept;
|
||||
if (isObject) {
|
||||
return metatype as NestInterceptor;
|
||||
return interceptor as NestInterceptor;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(
|
||||
metatype as Type<unknown>,
|
||||
);
|
||||
const instanceWrapper = this.getInstanceByMetatype(interceptor);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
@@ -80,8 +78,8 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
public getInstanceByMetatype(
|
||||
metatype: Type<unknown>,
|
||||
public getInstanceByMetatype<T extends Record<string, any> = any>(
|
||||
metatype: T,
|
||||
): InstanceWrapper | undefined {
|
||||
if (!this.moduleContext) {
|
||||
return;
|
||||
@@ -91,7 +89,7 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
if (!moduleRef) {
|
||||
return;
|
||||
}
|
||||
return moduleRef.injectables.get(metatype);
|
||||
return moduleRef.injectables.get(metatype.name as string);
|
||||
}
|
||||
|
||||
public getGlobalMetadata<T extends unknown[]>(
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
RouteInfo,
|
||||
} from '@nestjs/common/interfaces/middleware';
|
||||
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
|
||||
import { iterate } from 'iterare';
|
||||
import { RoutesMapper } from './routes-mapper';
|
||||
import { filterMiddleware } from './utils';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
private readonly middlewareCollection = new Set<MiddlewareConfiguration>();
|
||||
|
||||
@@ -3,13 +3,9 @@ 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<InstanceToken, InstanceWrapper>
|
||||
>();
|
||||
private readonly middleware = new Map<string, Map<string, InstanceWrapper>>();
|
||||
private readonly configurationSets = new Map<
|
||||
string,
|
||||
Set<MiddlewareConfiguration>
|
||||
@@ -19,7 +15,7 @@ export class MiddlewareContainer {
|
||||
|
||||
public getMiddlewareCollection(
|
||||
moduleKey: string,
|
||||
): Map<InstanceToken, InstanceWrapper> {
|
||||
): Map<string, InstanceWrapper> {
|
||||
if (!this.middleware.has(moduleKey)) {
|
||||
const moduleRef = this.container.getModuleByKey(moduleKey);
|
||||
this.middleware.set(moduleKey, moduleRef.middlewares);
|
||||
@@ -40,14 +36,13 @@ export class MiddlewareContainer {
|
||||
|
||||
const configurations = configList || [];
|
||||
const insertMiddleware = <T extends Type<unknown>>(metatype: T) => {
|
||||
const token = metatype;
|
||||
const token = metatype.name;
|
||||
middleware.set(
|
||||
token,
|
||||
new InstanceWrapper({
|
||||
scope: this.getClassScope(metatype),
|
||||
name: token,
|
||||
metatype,
|
||||
token,
|
||||
name: token,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ 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,
|
||||
@@ -18,7 +19,7 @@ import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { Injector } from '../injector/injector';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { InstanceToken, Module } from '../injector/module';
|
||||
import { 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';
|
||||
@@ -69,22 +70,22 @@ export class MiddlewareModule {
|
||||
modules: Map<string, Module>,
|
||||
) {
|
||||
const moduleEntries = [...modules.entries()];
|
||||
const loadMiddlewareConfiguration = async ([moduleName, moduleRef]: [
|
||||
const loadMiddlewareConfiguration = async ([name, module]: [
|
||||
string,
|
||||
Module,
|
||||
]) => {
|
||||
await this.loadConfiguration(middlewareContainer, moduleRef, moduleName);
|
||||
await this.resolver.resolveInstances(moduleRef, moduleName);
|
||||
const instance = module.instance;
|
||||
await this.loadConfiguration(middlewareContainer, instance, name);
|
||||
await this.resolver.resolveInstances(module, name);
|
||||
};
|
||||
await Promise.all(moduleEntries.map(loadMiddlewareConfiguration));
|
||||
}
|
||||
|
||||
public async loadConfiguration(
|
||||
middlewareContainer: MiddlewareContainer,
|
||||
moduleRef: Module,
|
||||
instance: NestModule,
|
||||
moduleKey: string,
|
||||
) {
|
||||
const { instance } = moduleRef;
|
||||
if (!instance.configure) {
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +164,7 @@ export class MiddlewareModule {
|
||||
|
||||
for (const metatype of middlewareCollection) {
|
||||
const collection = middlewareContainer.getMiddlewareCollection(moduleKey);
|
||||
const instanceWrapper = collection.get(metatype);
|
||||
const instanceWrapper = collection.get(metatype.name);
|
||||
if (isUndefined(instanceWrapper)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
@@ -187,10 +188,10 @@ export class MiddlewareModule {
|
||||
method: RequestMethod,
|
||||
path: string,
|
||||
moduleRef: Module,
|
||||
collection: Map<InstanceToken, InstanceWrapper>,
|
||||
collection: Map<string, 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 { InstanceToken, Module } from '../injector/module';
|
||||
import { 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<InstanceToken, InstanceWrapper>,
|
||||
middleware: Map<string, InstanceWrapper>,
|
||||
moduleRef: Module,
|
||||
) {
|
||||
await this.instanceLoader.loadMiddleware(wrapper, middleware, moduleRef);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user