mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature() transient/request scope with circular deps
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
/*import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncClassApplicationModule } from '../src/async-options-class.module';
|
||||
@@ -36,3 +36,4 @@ describe('GraphQL (async class)', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
/*import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncExistingApplicationModule } from '../src/async-options-existing.module';
|
||||
@@ -36,3 +36,4 @@ describe('GraphQL (async existing)', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
/*import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncApplicationModule } from '../src/async-options.module';
|
||||
@@ -34,3 +34,4 @@ describe('GraphQL (async configuration)', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
/*import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
@@ -38,3 +38,4 @@ describe('GraphQL', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
21
integration/scopes/.gitignore
vendored
Normal file
21
integration/scopes/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# IDE
|
||||
/.idea
|
||||
/.awcache
|
||||
/.vscode
|
||||
|
||||
# misc
|
||||
npm-debug.log
|
||||
|
||||
# example
|
||||
/quick-start
|
||||
|
||||
# tests
|
||||
/test
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# dist
|
||||
/dist
|
||||
71
integration/scopes/e2e/circular-request-scope.spec.ts
Normal file
71
integration/scopes/e2e/circular-request-scope.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { INestApplication, Scope } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { HelloController } from '../src/circular-hello/hello.controller';
|
||||
import { HelloModule } from '../src/circular-hello/hello.module';
|
||||
import { HelloService } from '../src/circular-hello/hello.service';
|
||||
import { UsersService } from '../src/circular-hello/users/users.service';
|
||||
|
||||
class Meta {
|
||||
static COUNTER = 0;
|
||||
constructor(private readonly helloService: HelloService) {
|
||||
Meta.COUNTER++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Circular request scope', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
scope: Scope.REQUEST,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, async () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(HelloService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create provider for each inquirer`, async () => {
|
||||
expect(Meta.COUNTER).to.be.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
71
integration/scopes/e2e/circular-transient-scope.spec.ts
Normal file
71
integration/scopes/e2e/circular-transient-scope.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { INestApplication, Scope } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { HelloController } from '../src/circular-transient/hello.controller';
|
||||
import { HelloModule } from '../src/circular-transient/hello.module';
|
||||
import { HelloService } from '../src/circular-transient/hello.service';
|
||||
import { UsersService } from '../src/circular-transient/users/users.service';
|
||||
|
||||
class Meta {
|
||||
static COUNTER = 0;
|
||||
constructor(private readonly helloService: HelloService) {
|
||||
Meta.COUNTER++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Circular transient scope', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
scope: Scope.TRANSIENT,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, async () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(HelloService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create provider for each inquirer`, async () => {
|
||||
expect(Meta.COUNTER).to.be.eql(7);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
80
integration/scopes/e2e/request-scope.spec.ts
Normal file
80
integration/scopes/e2e/request-scope.spec.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { Guard } from '../src/hello/guards/request-scoped.guard';
|
||||
import { HelloController } from '../src/hello/hello.controller';
|
||||
import { HelloModule } from '../src/hello/hello.module';
|
||||
import { Interceptor } from '../src/hello/interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from '../src/hello/users/user-by-id.pipe';
|
||||
import { UsersService } from '../src/hello/users/users.service';
|
||||
|
||||
class Meta {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Meta.COUNTER++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Request scope', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, async () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should share static provider across requests`, async () => {
|
||||
expect(Meta.COUNTER).to.be.eql(1);
|
||||
});
|
||||
|
||||
it(`should create request scoped pipe for each request`, async () => {
|
||||
expect(UserByIdPipe.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create request scoped interceptor for each request`, async () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create request scoped guard for each request`, async () => {
|
||||
expect(Guard.COUNTER).to.be.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
81
integration/scopes/e2e/transient-scope.spec.ts
Normal file
81
integration/scopes/e2e/transient-scope.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { INestApplication, Scope } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { Guard } from '../src/transient/guards/request-scoped.guard';
|
||||
import { HelloController } from '../src/transient/hello.controller';
|
||||
import { HelloModule } from '../src/transient/hello.module';
|
||||
import { Interceptor } from '../src/transient/interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from '../src/transient/users/user-by-id.pipe';
|
||||
import { UsersService } from '../src/transient/users/users.service';
|
||||
|
||||
class Meta {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Meta.COUNTER++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Transient scope', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
scope: Scope.TRANSIENT,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, async () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create provider for each inquirer`, async () => {
|
||||
expect(Meta.COUNTER).to.be.eql(7);
|
||||
});
|
||||
|
||||
it(`should create transient pipe for each controller`, async () => {
|
||||
expect(UserByIdPipe.COUNTER).to.be.eql(2);
|
||||
});
|
||||
|
||||
it(`should create transient interceptor for each controller`, async () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(2);
|
||||
});
|
||||
|
||||
it(`should create transient guard for each controller`, async () => {
|
||||
expect(Guard.COUNTER).to.be.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
1775
integration/scopes/package-lock.json
generated
Normal file
1775
integration/scopes/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
integration/scopes/package.json
Normal file
27
integration/scopes/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "nest-typescript-starter",
|
||||
"version": "1.0.0",
|
||||
"description": "Nest TypeScript starter repository",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^5.0.0",
|
||||
"@nestjs/core": "^5.0.0",
|
||||
"@nestjs/microservices": "^5.0.0",
|
||||
"@nestjs/testing": "^5.0.0",
|
||||
"@nestjs/websockets": "^5.0.0",
|
||||
"class-transformer": "^0.1.7",
|
||||
"class-validator": "^0.7.2",
|
||||
"fastify": "^1.1.1",
|
||||
"reflect-metadata": "^0.1.12",
|
||||
"rxjs": "^6.0.0",
|
||||
"typescript": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.41",
|
||||
"supertest": "^3.0.0",
|
||||
"ts-node": "^6.0.0"
|
||||
}
|
||||
}
|
||||
7
integration/scopes/src/app.module.ts
Normal file
7
integration/scopes/src/app.module.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HelloModule } from './hello/hello.module';
|
||||
|
||||
@Module({
|
||||
imports: [HelloModule.forRoot({ provide: 'META', useValue: true })],
|
||||
})
|
||||
export class ApplicationModule {}
|
||||
10
integration/scopes/src/circular-hello/dto/test.dto.ts
Normal file
10
integration/scopes/src/circular-hello/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,21 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
30
integration/scopes/src/circular-hello/hello.controller.ts
Normal file
30
integration/scopes/src/circular-hello/hello.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { HelloService } from './hello.service';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Controller('hello')
|
||||
export class HelloController {
|
||||
static COUNTER = 0;
|
||||
constructor(
|
||||
private readonly helloService: HelloService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
HelloController.COUNTER++;
|
||||
}
|
||||
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return this.helloService.greeting();
|
||||
}
|
||||
}
|
||||
19
integration/scopes/src/circular-hello/hello.module.ts
Normal file
19
integration/scopes/src/circular-hello/hello.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { DynamicModule, Inject, Module, Provider } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController],
|
||||
providers: [HelloService, UsersService],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
static forRoot(meta: Provider): DynamicModule {
|
||||
return {
|
||||
module: HelloModule,
|
||||
providers: [meta],
|
||||
};
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/circular-hello/hello.service.ts
Normal file
13
integration/scopes/src/circular-hello/hello.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class HelloService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
HelloService.COUNTER++;
|
||||
}
|
||||
|
||||
greeting(): string {
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
static COUNTER = 0;
|
||||
constructor(private readonly usersService: UsersService) {
|
||||
UserByIdPipe.COUNTER++;
|
||||
}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
return this.usersService.findById(value);
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/circular-hello/users/users.service.ts
Normal file
13
integration/scopes/src/circular-hello/users/users.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UsersService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
UsersService.COUNTER++;
|
||||
}
|
||||
|
||||
findById(id: string) {
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
10
integration/scopes/src/circular-transient/dto/test.dto.ts
Normal file
10
integration/scopes/src/circular-transient/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,21 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { HelloService } from './hello.service';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Controller('hello')
|
||||
export class HelloController {
|
||||
static COUNTER = 0;
|
||||
constructor(
|
||||
private readonly helloService: HelloService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
HelloController.COUNTER++;
|
||||
}
|
||||
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return this.helloService.greeting();
|
||||
}
|
||||
}
|
||||
20
integration/scopes/src/circular-transient/hello.module.ts
Normal file
20
integration/scopes/src/circular-transient/hello.module.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { DynamicModule, Inject, Module, Provider } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { TestController } from './test.controller';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController, TestController],
|
||||
providers: [HelloService, UsersService],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
static forRoot(meta: Provider): DynamicModule {
|
||||
return {
|
||||
module: HelloModule,
|
||||
providers: [meta],
|
||||
};
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/circular-transient/hello.service.ts
Normal file
13
integration/scopes/src/circular-transient/hello.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class HelloService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
HelloService.COUNTER++;
|
||||
}
|
||||
|
||||
greeting(): string {
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
20
integration/scopes/src/circular-transient/test.controller.ts
Normal file
20
integration/scopes/src/circular-transient/test.controller.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
|
||||
@Controller('test')
|
||||
export class TestController {
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return 'hey';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
ArgumentMetadata,
|
||||
Injectable,
|
||||
PipeTransform,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
UserByIdPipe.COUNTER++;
|
||||
}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UsersService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
UsersService.COUNTER++;
|
||||
}
|
||||
|
||||
findById(id: string) {
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
10
integration/scopes/src/hello/dto/test.dto.ts
Normal file
10
integration/scopes/src/hello/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;
|
||||
}
|
||||
21
integration/scopes/src/hello/guards/request-scoped.guard.ts
Normal file
21
integration/scopes/src/hello/guards/request-scoped.guard.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
30
integration/scopes/src/hello/hello.controller.ts
Normal file
30
integration/scopes/src/hello/hello.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { HelloService } from './hello.service';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Controller('hello')
|
||||
export class HelloController {
|
||||
static COUNTER = 0;
|
||||
constructor(
|
||||
private readonly helloService: HelloService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
HelloController.COUNTER++;
|
||||
}
|
||||
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return this.helloService.greeting();
|
||||
}
|
||||
}
|
||||
19
integration/scopes/src/hello/hello.module.ts
Normal file
19
integration/scopes/src/hello/hello.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { DynamicModule, Inject, Module, Provider } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController],
|
||||
providers: [HelloService, UsersService],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
static forRoot(meta: Provider): DynamicModule {
|
||||
return {
|
||||
module: HelloModule,
|
||||
providers: [meta],
|
||||
};
|
||||
}
|
||||
}
|
||||
10
integration/scopes/src/hello/hello.service.ts
Normal file
10
integration/scopes/src/hello/hello.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class HelloService {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
greeting(): string {
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
14
integration/scopes/src/hello/users/user-by-id.pipe.ts
Normal file
14
integration/scopes/src/hello/users/user-by-id.pipe.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
static COUNTER = 0;
|
||||
constructor(private readonly usersService: UsersService) {
|
||||
UserByIdPipe.COUNTER++;
|
||||
}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
return this.usersService.findById(value);
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/hello/users/users.service.ts
Normal file
13
integration/scopes/src/hello/users/users.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UsersService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
UsersService.COUNTER++;
|
||||
}
|
||||
|
||||
findById(id: string) {
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
8
integration/scopes/src/main.ts
Normal file
8
integration/scopes/src/main.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ApplicationModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(ApplicationModule);
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
10
integration/scopes/src/transient/dto/test.dto.ts
Normal file
10
integration/scopes/src/transient/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,21 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
30
integration/scopes/src/transient/hello.controller.ts
Normal file
30
integration/scopes/src/transient/hello.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { HelloService } from './hello.service';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Controller('hello')
|
||||
export class HelloController {
|
||||
static COUNTER = 0;
|
||||
constructor(
|
||||
private readonly helloService: HelloService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
HelloController.COUNTER++;
|
||||
}
|
||||
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return this.helloService.greeting();
|
||||
}
|
||||
}
|
||||
20
integration/scopes/src/transient/hello.module.ts
Normal file
20
integration/scopes/src/transient/hello.module.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { DynamicModule, Inject, Module, Provider } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { TestController } from './test.controller';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController, TestController],
|
||||
providers: [HelloService, UsersService],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
static forRoot(meta: Provider): DynamicModule {
|
||||
return {
|
||||
module: HelloModule,
|
||||
providers: [meta],
|
||||
};
|
||||
}
|
||||
}
|
||||
10
integration/scopes/src/transient/hello.service.ts
Normal file
10
integration/scopes/src/transient/hello.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class HelloService {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
greeting(): string {
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
20
integration/scopes/src/transient/test.controller.ts
Normal file
20
integration/scopes/src/transient/test.controller.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
|
||||
@Controller('test')
|
||||
export class TestController {
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@Get()
|
||||
greeting(@Param('id', UserByIdPipe) id): string {
|
||||
return 'hey';
|
||||
}
|
||||
}
|
||||
18
integration/scopes/src/transient/users/user-by-id.pipe.ts
Normal file
18
integration/scopes/src/transient/users/user-by-id.pipe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
ArgumentMetadata,
|
||||
Injectable,
|
||||
PipeTransform,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
UserByIdPipe.COUNTER++;
|
||||
}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/transient/users/users.service.ts
Normal file
13
integration/scopes/src/transient/users/users.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UsersService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
UsersService.COUNTER++;
|
||||
}
|
||||
|
||||
findById(id: string) {
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
22
integration/scopes/tsconfig.json
Normal file
22
integration/scopes/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"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",
|
||||
]
|
||||
}
|
||||
53
integration/scopes/tslint.json
Normal file
53
integration/scopes/tslint.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
],
|
||||
"jsRules": {
|
||||
"no-unused-expression": true
|
||||
},
|
||||
"rules": {
|
||||
"eofline": false,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"ordered-imports": [
|
||||
false
|
||||
],
|
||||
"max-line-length": [
|
||||
150
|
||||
],
|
||||
"member-ordering": [
|
||||
false
|
||||
],
|
||||
"curly": false,
|
||||
"interface-name": [
|
||||
false
|
||||
],
|
||||
"array-type": [
|
||||
false
|
||||
],
|
||||
"member-access": [
|
||||
false
|
||||
],
|
||||
"no-empty-interface": false,
|
||||
"no-empty": false,
|
||||
"arrow-parens": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"no-unused-expression": false,
|
||||
"max-classes-per-file": [
|
||||
false
|
||||
],
|
||||
"variable-name": [
|
||||
false
|
||||
],
|
||||
"one-line": [
|
||||
false
|
||||
],
|
||||
"one-variable-per-declaration": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
export enum Scope {
|
||||
DEFAULT,
|
||||
TRANSIENT,
|
||||
REQUEST,
|
||||
/** @experimental */
|
||||
LAZY_ASYNC,
|
||||
}
|
||||
|
||||
export interface ScopeOptions {
|
||||
|
||||
35
packages/common/test/utils/http-exception-body.util.spec.ts
Normal file
35
packages/common/test/utils/http-exception-body.util.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { expect } from 'chai';
|
||||
import { createHttpExceptionBody } from '../../utils/http-exception-body.util';
|
||||
|
||||
describe('createHttpExceptionBody', () => {
|
||||
describe('when object has been passed', () => {
|
||||
it('should return expected object', () => {
|
||||
const object = {
|
||||
message: 'test',
|
||||
};
|
||||
expect(createHttpExceptionBody(object)).to.be.eql(object);
|
||||
});
|
||||
});
|
||||
describe('when string has been passed', () => {
|
||||
it('should return expected object', () => {
|
||||
const message = 'test';
|
||||
const status = 500;
|
||||
const error = 'error';
|
||||
expect(createHttpExceptionBody(message, error, status)).to.be.eql({
|
||||
message,
|
||||
error,
|
||||
statusCode: status,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('when nil has been passed', () => {
|
||||
it('should return expected object', () => {
|
||||
const status = 500;
|
||||
const error = 'error';
|
||||
expect(createHttpExceptionBody(null, error, status)).to.be.eql({
|
||||
error,
|
||||
statusCode: status,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import { expect } from 'chai';
|
||||
import { randomStringGenerator } from '../../utils/random-string-generator.util';
|
||||
|
||||
describe('randomStringGenerator', () => {
|
||||
it('should generate random string', () => {
|
||||
expect(randomStringGenerator()).to.be.string;
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,8 @@ import { isObject } from './shared.utils';
|
||||
|
||||
export const createHttpExceptionBody = (
|
||||
message: object | string,
|
||||
error: string,
|
||||
statusCode: number,
|
||||
error?: string,
|
||||
statusCode?: number,
|
||||
) => {
|
||||
if (!message) {
|
||||
return { statusCode, error };
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
PipeTransform,
|
||||
WebSocketAdapter,
|
||||
CanActivate,
|
||||
ExceptionFilter,
|
||||
NestInterceptor,
|
||||
CanActivate,
|
||||
PipeTransform,
|
||||
WebSocketAdapter,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigurationProvider } from '@nestjs/common/interfaces/configuration-provider.interface';
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
@@ -26,7 +27,8 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
.filter(
|
||||
instance => instance && (isFunction(instance.catch) || instance.name),
|
||||
)
|
||||
.map(filter => this.getFilterInstance(filter, contextId))
|
||||
.map(filter => this.getFilterInstance(filter, contextId, inquirerId))
|
||||
.filter(item => !!item)
|
||||
.map(instance => ({
|
||||
func: instance.catch.bind(instance),
|
||||
exceptionMetatypes: this.reflectCatchExceptions(instance),
|
||||
@@ -37,16 +39,20 @@ export class BaseExceptionFilterContext extends ContextCreator {
|
||||
public getFilterInstance(
|
||||
filter: Function | ExceptionFilter,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
inquirerId?: string,
|
||||
): ExceptionFilter | null {
|
||||
const isObject = (filter as ExceptionFilter).catch;
|
||||
if (isObject) {
|
||||
return filter;
|
||||
return filter as ExceptionFilter;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(filter);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,21 +10,21 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { MESSAGES } from '../constants';
|
||||
import { ApplicationReferenceHost } from './../helpers/application-ref-host';
|
||||
import { HttpAdapterHost } from '../helpers';
|
||||
|
||||
export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
|
||||
private static readonly logger = new Logger('ExceptionsHandler');
|
||||
|
||||
@Optional()
|
||||
@Inject()
|
||||
protected readonly applicationRefHost?: ApplicationReferenceHost;
|
||||
protected readonly httpAdapterHost?: HttpAdapterHost;
|
||||
|
||||
constructor(protected readonly applicationRef?: HttpServer) {}
|
||||
|
||||
catch(exception: T, host: ArgumentsHost) {
|
||||
const applicationRef =
|
||||
this.applicationRef ||
|
||||
(this.applicationRefHost && this.applicationRefHost.applicationRef);
|
||||
(this.httpAdapterHost && this.httpAdapterHost.httpAdapter);
|
||||
|
||||
if (!(exception instanceof HttpException)) {
|
||||
const body = {
|
||||
|
||||
@@ -24,21 +24,29 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): CanActivate[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(instance, callback, GUARDS_METADATA);
|
||||
return this.createContext(
|
||||
instance,
|
||||
callback,
|
||||
GUARDS_METADATA,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
}
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
}
|
||||
return iterate(metadata)
|
||||
.filter((guard: any) => guard && (guard.name || guard.canActivate))
|
||||
.map(guard => this.getGuardInstance(guard, contextId))
|
||||
.map(guard => this.getGuardInstance(guard, contextId, inquirerId))
|
||||
.filter((guard: CanActivate) => guard && isFunction(guard.canActivate))
|
||||
.toArray() as R;
|
||||
}
|
||||
@@ -46,16 +54,20 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
public getGuardInstance(
|
||||
guard: Function | CanActivate,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
inquirerId?: string,
|
||||
): CanActivate | null {
|
||||
const isObject = (guard as CanActivate).canActivate;
|
||||
if (isObject) {
|
||||
return guard;
|
||||
return guard as CanActivate;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(guard);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { HttpServer } from '@nestjs/common';
|
||||
|
||||
export class ApplicationReferenceHost<T extends HttpServer = any> {
|
||||
private _applicationRef: T;
|
||||
|
||||
set applicationRef(applicationRef: T) {
|
||||
this._applicationRef = applicationRef;
|
||||
}
|
||||
|
||||
get applicationRef(): T | undefined {
|
||||
return this._applicationRef;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ export abstract class ContextCreator {
|
||||
public abstract createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId?: ContextId,
|
||||
inquirerId?: string,
|
||||
): R;
|
||||
public getGlobalMetadata?<T extends any[]>(): T;
|
||||
|
||||
@@ -14,6 +15,7 @@ export abstract class ContextCreator {
|
||||
callback: (...args: any[]) => any,
|
||||
metadataKey: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): R {
|
||||
const globalMetadata =
|
||||
this.getGlobalMetadata && this.getGlobalMetadata<T>();
|
||||
@@ -23,9 +25,14 @@ export abstract class ContextCreator {
|
||||
...this.createConcreteContext<T, R>(
|
||||
globalMetadata || ([] as T),
|
||||
contextId,
|
||||
inquirerId,
|
||||
),
|
||||
...this.createConcreteContext<T, R>(classMetadata, contextId, inquirerId),
|
||||
...this.createConcreteContext<T, R>(
|
||||
methodMetadata,
|
||||
contextId,
|
||||
inquirerId,
|
||||
),
|
||||
...this.createConcreteContext<T, R>(classMetadata, contextId),
|
||||
...this.createConcreteContext<T, R>(methodMetadata, contextId),
|
||||
] as R;
|
||||
}
|
||||
|
||||
|
||||
13
packages/core/helpers/context-id-factory.ts
Normal file
13
packages/core/helpers/context-id-factory.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ContextId } from '../injector/instance-wrapper';
|
||||
|
||||
export function createContextId(): ContextId {
|
||||
/**
|
||||
* We are generating random identifier to track asynchronous
|
||||
* execution context. An identifier does not have to be neither unique
|
||||
* nor unpredictable because WeakMap uses objects as keys (reference comparison).
|
||||
* Thus, even though identifier number might be equal, WeakMap would properly
|
||||
* associate asynchronous context with its internal map values using object reference.
|
||||
* Object is automatically removed once request has been processed (closure).
|
||||
*/
|
||||
return { id: Math.random() };
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { ContextId } from './../injector/instance-wrapper';
|
||||
import { ParamProperties } from './context-utils';
|
||||
|
||||
export const HANDLER_METADATA_SYMBOL = Symbol.for('handler_metadata:cache');
|
||||
|
||||
export interface HandlerMetadata {
|
||||
argsLength: number;
|
||||
paramsOptions: (ParamProperties & { metatype?: any })[];
|
||||
paramtypes: any[];
|
||||
getParamsMetadata: (
|
||||
moduleKey: string,
|
||||
contextId?: ContextId,
|
||||
inquirerId?: string,
|
||||
) => (ParamProperties & { metatype?: any })[];
|
||||
fnHandleResponse: <TResult, TResponse>(
|
||||
result: TResult,
|
||||
res: TResponse,
|
||||
|
||||
13
packages/core/helpers/http-adapter-host.ts
Normal file
13
packages/core/helpers/http-adapter-host.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { AbstractHttpAdapter } from '../adapters/http-adapter';
|
||||
|
||||
export class HttpAdapterHost<T extends AbstractHttpAdapter = any> {
|
||||
private _httpAdapter: T;
|
||||
|
||||
set httpAdapter(httpAdapter: T) {
|
||||
this._httpAdapter = httpAdapter;
|
||||
}
|
||||
|
||||
get httpAdapter(): T | undefined {
|
||||
return this._httpAdapter;
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
export * from './application-ref-host';
|
||||
export * from './http-adapter-host';
|
||||
|
||||
@@ -16,4 +16,5 @@ export * from './middleware';
|
||||
export * from './nest-application';
|
||||
export * from './nest-application-context';
|
||||
export { NestFactory } from './nest-factory';
|
||||
export * from './router';
|
||||
export * from './services';
|
||||
|
||||
@@ -6,10 +6,11 @@ import { ApplicationConfig } from '../application-config';
|
||||
import { CircularDependencyException } from '../errors/exceptions/circular-dependency.exception';
|
||||
import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception';
|
||||
import { UnknownModuleException } from '../errors/exceptions/unknown-module.exception';
|
||||
import { ApplicationReferenceHost } from '../helpers/application-ref-host';
|
||||
import { ExternalContextCreator } from '../helpers/external-context-creator';
|
||||
import { Reflector } from '../services';
|
||||
import { HttpAdapterHost } from '../helpers/http-adapter-host';
|
||||
import { ModuleCompiler } from './compiler';
|
||||
import { InternalCoreModule } from './internal-core-module';
|
||||
import { InternalProvidersStorage } from './internal-providers-storage';
|
||||
import { Module } from './module';
|
||||
import { ModulesContainer } from './modules-container';
|
||||
|
||||
@@ -21,11 +22,8 @@ export class NestContainer {
|
||||
string,
|
||||
Partial<DynamicModule>
|
||||
>();
|
||||
private readonly reflector = new Reflector();
|
||||
private readonly applicationRefHost = new ApplicationReferenceHost();
|
||||
private externalContextCreator: ExternalContextCreator;
|
||||
private modulesContainer: ModulesContainer;
|
||||
private applicationRef: any;
|
||||
private readonly internalProvidersStorage = new InternalProvidersStorage();
|
||||
private internalCoreModule: Module;
|
||||
|
||||
constructor(
|
||||
private readonly _applicationConfig: ApplicationConfig = undefined,
|
||||
@@ -35,23 +33,24 @@ export class NestContainer {
|
||||
return this._applicationConfig;
|
||||
}
|
||||
|
||||
public setApplicationRef(applicationRef: any) {
|
||||
this.applicationRef = applicationRef;
|
||||
public setHttpAdapter(httpAdapter: any) {
|
||||
this.internalProvidersStorage.httpAdapter = httpAdapter;
|
||||
|
||||
if (!this.applicationRefHost) {
|
||||
if (!this.internalProvidersStorage.httpAdapterHost) {
|
||||
return;
|
||||
}
|
||||
this.applicationRefHost.applicationRef = applicationRef;
|
||||
const host = this.internalProvidersStorage.httpAdapterHost;
|
||||
host.httpAdapter = httpAdapter;
|
||||
}
|
||||
|
||||
public getApplicationRef() {
|
||||
return this.applicationRef;
|
||||
public getHttpAdapterRef() {
|
||||
return this.internalProvidersStorage.httpAdapter;
|
||||
}
|
||||
|
||||
public async addModule(
|
||||
metatype: Type<any> | DynamicModule | Promise<DynamicModule>,
|
||||
scope: Type<any>[],
|
||||
) {
|
||||
): Promise<Module> {
|
||||
if (!metatype) {
|
||||
throw new InvalidModuleException(scope);
|
||||
}
|
||||
@@ -67,6 +66,8 @@ export class NestContainer {
|
||||
|
||||
this.addDynamicMetadata(token, dynamicMetadata, [].concat(scope, type));
|
||||
this.isGlobalModule(type) && this.addGlobalModule(module);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
public addDynamicMetadata(
|
||||
@@ -103,7 +104,11 @@ export class NestContainer {
|
||||
}
|
||||
|
||||
public getModuleByKey(moduleKey: string): Module {
|
||||
return this.modulesContainer.get(moduleKey);
|
||||
return this.modules.get(moduleKey);
|
||||
}
|
||||
|
||||
public getInternalCoreModuleRef(): Module | undefined {
|
||||
return this.internalCoreModule;
|
||||
}
|
||||
|
||||
public async addImport(
|
||||
@@ -198,25 +203,24 @@ export class NestContainer {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getReflector(): Reflector {
|
||||
return this.reflector;
|
||||
public createCoreModule(): DynamicModule {
|
||||
return InternalCoreModule.register([
|
||||
{
|
||||
provide: ExternalContextCreator,
|
||||
useValue: ExternalContextCreator.fromContainer(this),
|
||||
},
|
||||
{
|
||||
provide: ModulesContainer,
|
||||
useValue: this.modules,
|
||||
},
|
||||
{
|
||||
provide: HttpAdapterHost,
|
||||
useValue: this.internalProvidersStorage.httpAdapterHost,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public getExternalContextCreator(): ExternalContextCreator {
|
||||
if (!this.externalContextCreator) {
|
||||
this.externalContextCreator = ExternalContextCreator.fromContainer(this);
|
||||
}
|
||||
return this.externalContextCreator;
|
||||
}
|
||||
|
||||
public getApplicationRefHost(): ApplicationReferenceHost {
|
||||
return this.applicationRefHost;
|
||||
}
|
||||
|
||||
public getModulesContainer(): ModulesContainer {
|
||||
if (!this.modulesContainer) {
|
||||
this.modulesContainer = this.getModules();
|
||||
}
|
||||
return this.modulesContainer;
|
||||
public registerCoreModuleRef(moduleRef: Module) {
|
||||
this.internalCoreModule = moduleRef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './container';
|
||||
export * from './module-ref';
|
||||
export * from './modules-container';
|
||||
export * from './tokens';
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ContextId,
|
||||
InstancePerContext,
|
||||
InstanceWrapper,
|
||||
PropertyMetadata,
|
||||
} from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
@@ -70,6 +71,7 @@ export class Injector {
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const { metatype } = wrapper;
|
||||
const targetWrapper = collection.get(metatype.name);
|
||||
@@ -87,6 +89,7 @@ export class Injector {
|
||||
null,
|
||||
loadInstance,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,13 +104,16 @@ export class Injector {
|
||||
controllers,
|
||||
module,
|
||||
contextId,
|
||||
wrapper,
|
||||
);
|
||||
await this.loadEnhancersPerContext(wrapper, module, contextId, wrapper);
|
||||
}
|
||||
|
||||
public async loadInjectable(
|
||||
wrapper: InstanceWrapper<Controller>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const injectables = module.injectables;
|
||||
await this.loadInstance<Controller>(
|
||||
@@ -115,6 +121,7 @@ export class Injector {
|
||||
injectables,
|
||||
module,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,35 +129,36 @@ export class Injector {
|
||||
wrapper: InstanceWrapper<Injectable>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const providers = module.providers;
|
||||
await this.loadInstance<Injectable>(wrapper, providers, module, contextId);
|
||||
await this.loadInstance<Injectable>(
|
||||
wrapper,
|
||||
providers,
|
||||
module,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
await this.loadEnhancersPerContext(wrapper, module, contextId, wrapper);
|
||||
}
|
||||
|
||||
public loadPrototype<T>(
|
||||
{ metatype, name }: InstanceWrapper<T>,
|
||||
{ name }: InstanceWrapper<T>,
|
||||
collection: Map<string, InstanceWrapper<T>>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
if (!collection) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
const target = collection.get(name);
|
||||
const instanceHost = target.getInstanceByContextId(contextId);
|
||||
if (
|
||||
instanceHost.isResolved ||
|
||||
!isNil(target.inject) ||
|
||||
!metatype.prototype
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
collection.set(
|
||||
name,
|
||||
new InstanceWrapper({
|
||||
const instance = target.createPrototype(contextId);
|
||||
if (instance) {
|
||||
const wrapper = new InstanceWrapper({
|
||||
...target,
|
||||
instance: Object.create(metatype.prototype),
|
||||
}),
|
||||
);
|
||||
instance,
|
||||
});
|
||||
collection.set(name, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
public applyDoneHook<T>(wrapper: InstancePerContext<T>): () => void {
|
||||
@@ -167,8 +175,10 @@ export class Injector {
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const instanceHost = wrapper.getInstanceByContextId(contextId);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const instanceHost = wrapper.getInstanceByContextId(contextId, inquirerId);
|
||||
if (instanceHost.isPending) {
|
||||
return instanceHost.donePromise;
|
||||
}
|
||||
@@ -188,12 +198,14 @@ export class Injector {
|
||||
module,
|
||||
inject,
|
||||
contextId,
|
||||
wrapper,
|
||||
);
|
||||
const instance = await this.instantiateClass(
|
||||
instances,
|
||||
wrapper,
|
||||
targetWrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
this.applyProperties(instance, properties);
|
||||
done();
|
||||
@@ -204,6 +216,7 @@ export class Injector {
|
||||
inject,
|
||||
callback,
|
||||
contextId,
|
||||
wrapper,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -213,20 +226,14 @@ export class Injector {
|
||||
inject: InjectorDependency[],
|
||||
callback: (args: any[]) => void,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const metadata = wrapper.getCtorMetadata();
|
||||
if (metadata) {
|
||||
const dependenciesHosts = await Promise.all(
|
||||
metadata.map(async item =>
|
||||
this.resolveComponentHost(item.host, item, contextId),
|
||||
),
|
||||
);
|
||||
const deps = dependenciesHosts.map(
|
||||
item => item.getInstanceByContextId(contextId).instance,
|
||||
);
|
||||
if (metadata && contextId !== STATIC_CONTEXT) {
|
||||
const deps = await this.loadCtorMetadata(metadata, contextId, inquirer);
|
||||
return callback(deps);
|
||||
}
|
||||
|
||||
const dependencies = isNil(inject)
|
||||
? this.reflectConstructorParams(wrapper.metatype)
|
||||
: inject;
|
||||
@@ -235,8 +242,7 @@ export class Injector {
|
||||
: [];
|
||||
|
||||
let isResolved = true;
|
||||
|
||||
const findOneParam = async (param, index) => {
|
||||
const resolveParam = async (param, index) => {
|
||||
try {
|
||||
const paramWrapper = await this.resolveSingleParam<T>(
|
||||
wrapper,
|
||||
@@ -244,8 +250,12 @@ export class Injector {
|
||||
{ index, dependencies },
|
||||
module,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(contextId);
|
||||
if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
|
||||
isResolved = false;
|
||||
}
|
||||
@@ -259,7 +269,7 @@ export class Injector {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
const instances = await Promise.all(dependencies.map(findOneParam));
|
||||
const instances = await Promise.all(dependencies.map(resolveParam));
|
||||
isResolved && (await callback(instances));
|
||||
}
|
||||
|
||||
@@ -285,6 +295,7 @@ export class Injector {
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
module: Module,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
if (isUndefined(param)) {
|
||||
throw new UndefinedDependencyException(
|
||||
@@ -300,6 +311,7 @@ export class Injector {
|
||||
dependencyContext,
|
||||
wrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -320,6 +332,7 @@ export class Injector {
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<InstanceWrapper> {
|
||||
const providers = module.providers;
|
||||
const instanceWrapper = await this.lookupComponent(
|
||||
@@ -328,23 +341,53 @@ export class Injector {
|
||||
{ ...dependencyContext, name },
|
||||
wrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
return this.resolveComponentHost(
|
||||
module,
|
||||
instanceWrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
return this.resolveComponentHost(module, instanceWrapper, contextId);
|
||||
}
|
||||
|
||||
public async resolveComponentHost<T>(
|
||||
module: Module,
|
||||
instanceWrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<InstanceWrapper> {
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
|
||||
await this.loadProvider(instanceWrapper, module, contextId);
|
||||
await this.loadProvider(instanceWrapper, module, contextId, inquirer);
|
||||
} else if (
|
||||
!instanceHost.isResolved &&
|
||||
instanceWrapper.forwardRef &&
|
||||
(contextId !== STATIC_CONTEXT || !!inquirerId)
|
||||
) {
|
||||
/**
|
||||
* When circular dependency has been detected between
|
||||
* either request/transient providers, we have to asynchronously
|
||||
* resolve instance host for a specific contextId or inquirer, to ensure
|
||||
* that eventual lazily created instance will be merged with the prototype
|
||||
* instantiated beforehand.
|
||||
*/
|
||||
instanceHost.donePromise &&
|
||||
instanceHost.donePromise.then(() =>
|
||||
this.loadProvider(instanceWrapper, module, contextId, inquirer),
|
||||
);
|
||||
}
|
||||
if (instanceWrapper.async) {
|
||||
const host = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const host = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
host.instance = await host.instance;
|
||||
instanceWrapper.setInstanceByContextId(contextId, host);
|
||||
instanceWrapper.setInstanceByContextId(contextId, host, inquirerId);
|
||||
}
|
||||
return instanceWrapper;
|
||||
}
|
||||
@@ -355,6 +398,7 @@ export class Injector {
|
||||
dependencyContext: InjectorDependencyContext,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<InstanceWrapper<T>> {
|
||||
const { name } = dependencyContext;
|
||||
const scanInExports = () =>
|
||||
@@ -363,8 +407,8 @@ export class Injector {
|
||||
module,
|
||||
wrapper,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
|
||||
return providers.has(name) ? providers.get(name) : scanInExports();
|
||||
}
|
||||
|
||||
@@ -373,6 +417,7 @@ export class Injector {
|
||||
module: Module,
|
||||
wrapper: InstanceWrapper<T>,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const instanceWrapper = await this.lookupComponentInImports(
|
||||
module,
|
||||
@@ -380,6 +425,7 @@ export class Injector {
|
||||
wrapper,
|
||||
[],
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
if (isNil(instanceWrapper)) {
|
||||
throw new UnknownDependenciesException(
|
||||
@@ -397,10 +443,11 @@ export class Injector {
|
||||
wrapper: InstanceWrapper,
|
||||
moduleRegistry: any[] = [],
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<any> {
|
||||
let instanceWrapperRef: InstanceWrapper = null;
|
||||
|
||||
const imports: Set<Module> = module.imports || new Set();
|
||||
const imports = module.imports || new Set<Module>();
|
||||
const children = [...imports.values()].filter(item => item);
|
||||
|
||||
for (const relatedModule of children) {
|
||||
@@ -416,6 +463,7 @@ export class Injector {
|
||||
wrapper,
|
||||
moduleRegistry,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
if (instanceRef) {
|
||||
return instanceRef;
|
||||
@@ -424,9 +472,18 @@ export class Injector {
|
||||
}
|
||||
instanceWrapperRef = providers.get(name);
|
||||
|
||||
const instanceHost = instanceWrapperRef.getInstanceByContextId(contextId);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const instanceHost = instanceWrapperRef.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
|
||||
await this.loadProvider(instanceWrapperRef, relatedModule, contextId);
|
||||
await this.loadProvider(
|
||||
instanceWrapperRef,
|
||||
relatedModule,
|
||||
contextId,
|
||||
wrapper,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -438,23 +495,14 @@ export class Injector {
|
||||
module: Module,
|
||||
inject?: InjectorDependency[],
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<PropertyDependency[]> {
|
||||
if (!isNil(inject)) {
|
||||
return [];
|
||||
}
|
||||
const metadata = wrapper.getPropertiesMetadata();
|
||||
if (metadata) {
|
||||
const dependenciesHosts = await Promise.all(
|
||||
metadata.map(async ({ wrapper: item, key }) => ({
|
||||
key,
|
||||
host: await this.resolveComponentHost(item.host, item, contextId),
|
||||
})),
|
||||
);
|
||||
return dependenciesHosts.map(({ key, host }) => ({
|
||||
key,
|
||||
name: key,
|
||||
instance: host.getInstanceByContextId(contextId).instance,
|
||||
}));
|
||||
if (metadata && contextId !== STATIC_CONTEXT) {
|
||||
return this.loadPropertiesMetadata(metadata, contextId, inquirer);
|
||||
}
|
||||
const properties = this.reflectProperties(wrapper.metatype);
|
||||
const instances = await Promise.all(
|
||||
@@ -470,12 +518,18 @@ export class Injector {
|
||||
dependencyContext,
|
||||
module,
|
||||
contextId,
|
||||
inquirer,
|
||||
);
|
||||
if (!paramWrapper) {
|
||||
return undefined;
|
||||
}
|
||||
wrapper.addPropertiesMetadata(item.key, paramWrapper);
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(contextId);
|
||||
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const instanceHost = paramWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return instanceHost.instance;
|
||||
} catch (err) {
|
||||
if (!item.isOptional) {
|
||||
@@ -520,16 +574,24 @@ export class Injector {
|
||||
wrapper: InstanceWrapper,
|
||||
targetMetatype: InstanceWrapper,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<T> {
|
||||
const { metatype, inject } = wrapper;
|
||||
const instanceHost = targetMetatype.getInstanceByContextId(contextId);
|
||||
const isDependencyTreeStatic = wrapper.isDependencyTreeStatic();
|
||||
const isInContext =
|
||||
(isDependencyTreeStatic && contextId === STATIC_CONTEXT) ||
|
||||
(!isDependencyTreeStatic && contextId !== STATIC_CONTEXT);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
const instanceHost = targetMetatype.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const isStatic = wrapper.isStatic(contextId, inquirer);
|
||||
const isInRequestScope = wrapper.isInRequestScope(contextId, inquirer);
|
||||
const isLazyTransient = wrapper.isLazyTransient(contextId, inquirer);
|
||||
const isInContext = isStatic || isInRequestScope || isLazyTransient;
|
||||
|
||||
if (isNil(inject) && isInContext) {
|
||||
const targetInstance = wrapper.getInstanceByContextId(contextId);
|
||||
const targetInstance = wrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
|
||||
targetInstance.instance = wrapper.forwardRef
|
||||
? Object.assign(targetInstance.instance, new metatype(...instances))
|
||||
@@ -544,7 +606,7 @@ export class Injector {
|
||||
return instanceHost.instance;
|
||||
}
|
||||
|
||||
async loadPerContext<T = any>(
|
||||
public async loadPerContext<T = any>(
|
||||
instance: T,
|
||||
module: Module,
|
||||
collection: Map<string, InstanceWrapper>,
|
||||
@@ -553,21 +615,66 @@ export class Injector {
|
||||
const wrapper = collection.get(
|
||||
instance.constructor && instance.constructor.name,
|
||||
);
|
||||
await this.loadInstance(wrapper, collection, module, ctx);
|
||||
await this.loadEnhancersPerContext(wrapper, module, ctx);
|
||||
await this.loadInstance(wrapper, collection, module, ctx, wrapper);
|
||||
await this.loadEnhancersPerContext(wrapper, module, ctx, wrapper);
|
||||
|
||||
const host = wrapper.getInstanceByContextId(ctx);
|
||||
return host && (host.instance as T);
|
||||
}
|
||||
|
||||
async loadEnhancersPerContext(
|
||||
public async loadEnhancersPerContext(
|
||||
wrapper: InstanceWrapper,
|
||||
module: Module,
|
||||
ctx: ContextId,
|
||||
inquirer?: InstanceWrapper,
|
||||
) {
|
||||
const enhancers = wrapper.getEnhancersMetadata();
|
||||
const enhancers = wrapper.getEnhancersMetadata() || [];
|
||||
const loadEnhancer = (item: InstanceWrapper) =>
|
||||
this.loadInstance(item, module.injectables, module, ctx);
|
||||
this.loadInstance(item, module.injectables, module, ctx, inquirer);
|
||||
await Promise.all(enhancers.map(loadEnhancer));
|
||||
}
|
||||
|
||||
public async loadCtorMetadata(
|
||||
metadata: InstanceWrapper<any>[],
|
||||
contextId: ContextId,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<any[]> {
|
||||
const hosts = await Promise.all(
|
||||
metadata.map(async item =>
|
||||
this.resolveComponentHost(item.host, item, contextId, inquirer),
|
||||
),
|
||||
);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
return hosts.map(
|
||||
item => item.getInstanceByContextId(contextId, inquirerId).instance,
|
||||
);
|
||||
}
|
||||
|
||||
public async loadPropertiesMetadata(
|
||||
metadata: PropertyMetadata[],
|
||||
contextId: ContextId,
|
||||
inquirer?: InstanceWrapper,
|
||||
): Promise<PropertyDependency[]> {
|
||||
const dependenciesHosts = await Promise.all(
|
||||
metadata.map(async ({ wrapper: item, key }) => ({
|
||||
key,
|
||||
host: await this.resolveComponentHost(
|
||||
item.host,
|
||||
item,
|
||||
contextId,
|
||||
inquirer,
|
||||
),
|
||||
})),
|
||||
);
|
||||
const inquirerId = this.getInquirerId(inquirer);
|
||||
return dependenciesHosts.map(({ key, host }) => ({
|
||||
key,
|
||||
name: key,
|
||||
instance: host.getInstanceByContextId(contextId, inquirerId).instance,
|
||||
}));
|
||||
}
|
||||
|
||||
private getInquirerId(inquirer: InstanceWrapper | undefined): string {
|
||||
return inquirer && inquirer.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Scope, Type } from '@nestjs/common';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { isNil, isUndefined } from '@nestjs/common/utils/shared.utils';
|
||||
import { STATIC_CONTEXT } from './constants';
|
||||
import { Module } from './module';
|
||||
|
||||
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
|
||||
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
|
||||
|
||||
export interface ContextId {
|
||||
readonly id: number;
|
||||
@@ -36,13 +39,23 @@ export class InstanceWrapper<T = any> {
|
||||
|
||||
private readonly values = new WeakMap<ContextId, InstancePerContext<T>>();
|
||||
private readonly [INSTANCE_METADATA_SYMBOL]: InstanceMetadataStore = {};
|
||||
private readonly [INSTANCE_ID_SYMBOL]: string;
|
||||
private transientMap?:
|
||||
| Map<string, WeakMap<ContextId, InstancePerContext<T>>>
|
||||
| undefined;
|
||||
private isTreeStatic: boolean | undefined;
|
||||
|
||||
constructor(
|
||||
metadata: Partial<InstanceWrapper<T>> & Partial<InstancePerContext<T>> = {},
|
||||
) {
|
||||
this[INSTANCE_ID_SYMBOL] = randomStringGenerator();
|
||||
this.initialize(metadata);
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this[INSTANCE_ID_SYMBOL];
|
||||
}
|
||||
|
||||
set instance(value: T) {
|
||||
this.values.set(STATIC_CONTEXT, { instance: value });
|
||||
}
|
||||
@@ -56,29 +69,74 @@ export class InstanceWrapper<T = any> {
|
||||
return !this.metatype;
|
||||
}
|
||||
|
||||
getInstanceByContextId(contextId: ContextId): InstancePerContext<T> {
|
||||
get isTransient(): boolean {
|
||||
return this.scope === Scope.TRANSIENT;
|
||||
}
|
||||
|
||||
public getInstanceByContextId(
|
||||
contextId: ContextId,
|
||||
inquirerId?: string,
|
||||
): InstancePerContext<T> {
|
||||
if (this.scope === Scope.TRANSIENT && inquirerId) {
|
||||
return this.getInstanceByInquirerId(contextId, inquirerId);
|
||||
}
|
||||
const instancePerContext = this.values.get(contextId);
|
||||
return instancePerContext
|
||||
? instancePerContext
|
||||
: this.cloneStaticInstance(contextId);
|
||||
}
|
||||
|
||||
setInstanceByContextId(contextId: ContextId, value: InstancePerContext<T>) {
|
||||
public getInstanceByInquirerId(
|
||||
contextId: ContextId,
|
||||
inquirerId: string,
|
||||
): InstancePerContext<T> {
|
||||
let collectionPerContext = this.transientMap.get(inquirerId);
|
||||
if (!collectionPerContext) {
|
||||
collectionPerContext = new WeakMap();
|
||||
this.transientMap.set(inquirerId, collectionPerContext);
|
||||
}
|
||||
const instancePerContext = collectionPerContext.get(contextId);
|
||||
return instancePerContext
|
||||
? instancePerContext
|
||||
: this.cloneTransientInstance(contextId, inquirerId);
|
||||
}
|
||||
|
||||
public setInstanceByContextId(
|
||||
contextId: ContextId,
|
||||
value: InstancePerContext<T>,
|
||||
inquirerId?: string,
|
||||
) {
|
||||
if (this.scope === Scope.TRANSIENT && inquirerId) {
|
||||
return this.setInstanceByInquirerId(contextId, inquirerId, value);
|
||||
}
|
||||
this.values.set(contextId, value);
|
||||
}
|
||||
|
||||
addCtorMetadata(index: number, wrapper: InstanceWrapper) {
|
||||
public setInstanceByInquirerId(
|
||||
contextId: ContextId,
|
||||
inquirerId: string,
|
||||
value: InstancePerContext<T>,
|
||||
) {
|
||||
let collection = this.transientMap.get(inquirerId);
|
||||
if (!collection) {
|
||||
collection = new WeakMap();
|
||||
this.transientMap.set(inquirerId, collection);
|
||||
}
|
||||
collection.set(contextId, value);
|
||||
}
|
||||
|
||||
public addCtorMetadata(index: number, wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].dependencies) {
|
||||
this[INSTANCE_METADATA_SYMBOL].dependencies = [];
|
||||
}
|
||||
this[INSTANCE_METADATA_SYMBOL].dependencies[index] = wrapper;
|
||||
}
|
||||
|
||||
getCtorMetadata(): InstanceWrapper[] {
|
||||
public getCtorMetadata(): InstanceWrapper[] {
|
||||
return this[INSTANCE_METADATA_SYMBOL].dependencies;
|
||||
}
|
||||
|
||||
addPropertiesMetadata(key: string, wrapper: InstanceWrapper) {
|
||||
public addPropertiesMetadata(key: string, wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].properties) {
|
||||
this[INSTANCE_METADATA_SYMBOL].properties = [];
|
||||
}
|
||||
@@ -88,44 +146,163 @@ export class InstanceWrapper<T = any> {
|
||||
});
|
||||
}
|
||||
|
||||
getPropertiesMetadata(): PropertyMetadata[] {
|
||||
public getPropertiesMetadata(): PropertyMetadata[] {
|
||||
return this[INSTANCE_METADATA_SYMBOL].properties;
|
||||
}
|
||||
|
||||
addEnhancerMetadata(wrapper: InstanceWrapper) {
|
||||
public addEnhancerMetadata(wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].enhancers) {
|
||||
this[INSTANCE_METADATA_SYMBOL].enhancers = [];
|
||||
}
|
||||
this[INSTANCE_METADATA_SYMBOL].enhancers.push(wrapper);
|
||||
}
|
||||
|
||||
getEnhancersMetadata(): InstanceWrapper[] {
|
||||
public getEnhancersMetadata(): InstanceWrapper[] {
|
||||
return this[INSTANCE_METADATA_SYMBOL].enhancers;
|
||||
}
|
||||
|
||||
isDependencyTreeStatic(): boolean {
|
||||
if (this.scope === Scope.REQUEST) {
|
||||
return false;
|
||||
public isDependencyTreeStatic(lookupRegistry: string[] = []): boolean {
|
||||
if (!isUndefined(this.isTreeStatic)) {
|
||||
return this.isTreeStatic;
|
||||
}
|
||||
if (this.scope === Scope.REQUEST) {
|
||||
this.isTreeStatic = false;
|
||||
return this.isTreeStatic;
|
||||
}
|
||||
if (lookupRegistry.includes(this[INSTANCE_ID_SYMBOL])) {
|
||||
return true;
|
||||
}
|
||||
lookupRegistry = lookupRegistry.concat(this[INSTANCE_ID_SYMBOL]);
|
||||
|
||||
const { dependencies, properties, enhancers } = this[
|
||||
INSTANCE_METADATA_SYMBOL
|
||||
];
|
||||
let isStatic =
|
||||
(dependencies && this.isWrapperStatic(dependencies)) || !dependencies;
|
||||
(dependencies &&
|
||||
this.isWrapperListStatic(dependencies, lookupRegistry)) ||
|
||||
!dependencies;
|
||||
|
||||
if (!properties || !isStatic) {
|
||||
return isStatic;
|
||||
if (!isStatic || !(properties || enhancers)) {
|
||||
this.isTreeStatic = isStatic;
|
||||
return this.isTreeStatic;
|
||||
}
|
||||
const propertiesHosts = properties.map(item => item.wrapper);
|
||||
isStatic = isStatic && this.isWrapperStatic(propertiesHosts);
|
||||
if (!enhancers || !isStatic) {
|
||||
return isStatic;
|
||||
const propertiesHosts = (properties || []).map(item => item.wrapper);
|
||||
isStatic =
|
||||
isStatic && this.isWrapperListStatic(propertiesHosts, lookupRegistry);
|
||||
if (!isStatic || !enhancers) {
|
||||
this.isTreeStatic = isStatic;
|
||||
return this.isTreeStatic;
|
||||
}
|
||||
return this.isWrapperStatic(enhancers);
|
||||
this.isTreeStatic = this.isWrapperListStatic(enhancers, lookupRegistry);
|
||||
return this.isTreeStatic;
|
||||
}
|
||||
|
||||
private isWrapperStatic(tree: InstanceWrapper[]) {
|
||||
return tree.every((item: InstanceWrapper) => item.isDependencyTreeStatic());
|
||||
public cloneStaticInstance(contextId: ContextId): InstancePerContext<T> {
|
||||
const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
|
||||
if (this.isDependencyTreeStatic()) {
|
||||
return staticInstance;
|
||||
}
|
||||
const instancePerContext: InstancePerContext<T> = {
|
||||
...staticInstance,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
isPending: false,
|
||||
};
|
||||
if (this.isNewable()) {
|
||||
instancePerContext.instance = Object.create(this.metatype.prototype);
|
||||
}
|
||||
this.setInstanceByContextId(contextId, instancePerContext);
|
||||
return instancePerContext;
|
||||
}
|
||||
|
||||
public cloneTransientInstance(
|
||||
contextId: ContextId,
|
||||
inquirerId: string,
|
||||
): InstancePerContext<T> {
|
||||
const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
|
||||
const instancePerContext: InstancePerContext<T> = {
|
||||
...staticInstance,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
isPending: false,
|
||||
};
|
||||
if (this.isNewable()) {
|
||||
instancePerContext.instance = Object.create(this.metatype.prototype);
|
||||
}
|
||||
this.setInstanceByInquirerId(contextId, inquirerId, instancePerContext);
|
||||
return instancePerContext;
|
||||
}
|
||||
|
||||
public createPrototype(contextId: ContextId) {
|
||||
const host = this.getInstanceByContextId(contextId);
|
||||
if (!this.isNewable() || host.isResolved) {
|
||||
return;
|
||||
}
|
||||
return Object.create(this.metatype.prototype);
|
||||
}
|
||||
|
||||
public isInRequestScope(
|
||||
contextId: ContextId,
|
||||
inquirer?: InstanceWrapper | undefined,
|
||||
): boolean {
|
||||
const isDependencyTreeStatic = this.isDependencyTreeStatic();
|
||||
|
||||
return ((!isDependencyTreeStatic &&
|
||||
contextId !== STATIC_CONTEXT &&
|
||||
(!this.isTransient || (this.isTransient && inquirer))) as any) as boolean;
|
||||
}
|
||||
|
||||
public isLazyTransient(
|
||||
contextId: ContextId,
|
||||
inquirer: InstanceWrapper | undefined,
|
||||
): boolean {
|
||||
const isInquirerRequestScoped =
|
||||
inquirer && inquirer.scope === Scope.REQUEST;
|
||||
|
||||
return (
|
||||
this.isDependencyTreeStatic() &&
|
||||
contextId !== STATIC_CONTEXT &&
|
||||
this.isTransient &&
|
||||
isInquirerRequestScoped
|
||||
);
|
||||
}
|
||||
|
||||
public isStatic(
|
||||
contextId: ContextId,
|
||||
inquirer: InstanceWrapper | undefined,
|
||||
): boolean {
|
||||
const isInquirerRequestScoped =
|
||||
inquirer && inquirer.scope === Scope.REQUEST;
|
||||
const isStaticTransient = this.isTransient && !isInquirerRequestScoped;
|
||||
|
||||
return (
|
||||
this.isDependencyTreeStatic() &&
|
||||
contextId === STATIC_CONTEXT &&
|
||||
(!this.isTransient || (isStaticTransient && !!inquirer))
|
||||
);
|
||||
}
|
||||
|
||||
public getStaticTransientInstances() {
|
||||
if (!this.transientMap) {
|
||||
return [];
|
||||
}
|
||||
const instances = [...this.transientMap.values()];
|
||||
return instances
|
||||
.map(item => item.get(STATIC_CONTEXT))
|
||||
.filter(item => !!item);
|
||||
}
|
||||
|
||||
private isNewable(): boolean {
|
||||
return isNil(this.inject) && this.metatype && this.metatype.prototype;
|
||||
}
|
||||
|
||||
private isWrapperListStatic(
|
||||
tree: InstanceWrapper[],
|
||||
lookupRegistry: string[],
|
||||
): boolean {
|
||||
return tree.every((item: InstanceWrapper) =>
|
||||
item.isDependencyTreeStatic(lookupRegistry),
|
||||
);
|
||||
}
|
||||
|
||||
private initialize(
|
||||
@@ -138,20 +315,6 @@ export class InstanceWrapper<T = any> {
|
||||
instance,
|
||||
isResolved,
|
||||
});
|
||||
}
|
||||
|
||||
private cloneStaticInstance(contextId: ContextId): InstancePerContext<T> {
|
||||
const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
|
||||
if (this.isDependencyTreeStatic()) {
|
||||
return staticInstance;
|
||||
}
|
||||
const instancePerContext: InstancePerContext<T> = {
|
||||
...staticInstance,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
isPending: false,
|
||||
};
|
||||
this.setInstanceByContextId(contextId, instancePerContext);
|
||||
return instancePerContext;
|
||||
this.scope === Scope.TRANSIENT && (this.transientMap = new Map());
|
||||
}
|
||||
}
|
||||
|
||||
19
packages/core/injector/internal-core-module.ts
Normal file
19
packages/core/injector/internal-core-module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { DynamicModule, Global, Module } from '@nestjs/common';
|
||||
import { ValueProvider } from '@nestjs/common/interfaces';
|
||||
import { requestProvider } from './../router/request/request-providers';
|
||||
import { Reflector } from './../services';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [Reflector, requestProvider],
|
||||
exports: [Reflector, requestProvider],
|
||||
})
|
||||
export class InternalCoreModule {
|
||||
static register(providers: ValueProvider[]): DynamicModule {
|
||||
return {
|
||||
module: InternalCoreModule,
|
||||
providers: [...providers],
|
||||
exports: [...providers.map(item => item.provide)],
|
||||
};
|
||||
}
|
||||
}
|
||||
19
packages/core/injector/internal-providers-storage.ts
Normal file
19
packages/core/injector/internal-providers-storage.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { HttpAdapterHost } from '../helpers';
|
||||
import { AbstractHttpAdapter } from './../adapters';
|
||||
|
||||
export class InternalProvidersStorage {
|
||||
private readonly _httpAdapterHost = new HttpAdapterHost();
|
||||
private _httpAdapter: AbstractHttpAdapter;
|
||||
|
||||
get httpAdapterHost(): HttpAdapterHost {
|
||||
return this._httpAdapterHost;
|
||||
}
|
||||
|
||||
get httpAdapter(): AbstractHttpAdapter {
|
||||
return this._httpAdapter;
|
||||
}
|
||||
|
||||
set httpAdapter(httpAdapter: AbstractHttpAdapter) {
|
||||
this._httpAdapter = httpAdapter;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export abstract class ModuleRef {
|
||||
module: Module,
|
||||
): Promise<T> {
|
||||
const wrapper = new InstanceWrapper({
|
||||
name: type.name,
|
||||
name: type && type.name,
|
||||
metatype: type,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
|
||||
@@ -18,14 +18,9 @@ import {
|
||||
import { InvalidClassException } from '../errors/exceptions/invalid-class.exception';
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { UnknownExportException } from '../errors/exceptions/unknown-export.exception';
|
||||
import { ApplicationReferenceHost } from '../helpers/application-ref-host';
|
||||
import { ExternalContextCreator } from '../helpers/external-context-creator';
|
||||
import { Reflector } from '../services/reflector.service';
|
||||
import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { ModuleRef } from './module-ref';
|
||||
import { ModulesContainer } from './modules-container';
|
||||
import { HTTP_SERVER_REF } from './tokens';
|
||||
|
||||
export interface CustomProvider {
|
||||
provide: any;
|
||||
@@ -76,7 +71,7 @@ export class Module {
|
||||
return this._scope;
|
||||
}
|
||||
|
||||
get providers(): Map<string, InstanceWrapper<Injectable>> {
|
||||
get providers(): Map<any, InstanceWrapper<Injectable>> {
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
@@ -132,11 +127,6 @@ export class Module {
|
||||
public addCoreProviders(container: NestContainer) {
|
||||
this.addModuleAsProvider();
|
||||
this.addModuleRef();
|
||||
this.addReflector(container.getReflector());
|
||||
this.addApplicationRef(container.getApplicationRef());
|
||||
this.addExternalContextCreator(container.getExternalContextCreator());
|
||||
this.addModulesContainer(container.getModulesContainer());
|
||||
this.addApplicationRefHost(container.getApplicationRefHost());
|
||||
}
|
||||
|
||||
public addModuleRef() {
|
||||
@@ -166,73 +156,6 @@ export class Module {
|
||||
);
|
||||
}
|
||||
|
||||
public addReflector(reflector: Reflector) {
|
||||
this._providers.set(
|
||||
Reflector.name,
|
||||
new InstanceWrapper({
|
||||
name: Reflector.name,
|
||||
metatype: Reflector,
|
||||
isResolved: true,
|
||||
instance: reflector,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addApplicationRef(applicationRef: any) {
|
||||
this._providers.set(
|
||||
HTTP_SERVER_REF,
|
||||
new InstanceWrapper({
|
||||
name: HTTP_SERVER_REF,
|
||||
metatype: {} as any,
|
||||
isResolved: true,
|
||||
instance: applicationRef || {},
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addExternalContextCreator(
|
||||
externalContextCreator: ExternalContextCreator,
|
||||
) {
|
||||
this._providers.set(
|
||||
ExternalContextCreator.name,
|
||||
new InstanceWrapper({
|
||||
name: ExternalContextCreator.name,
|
||||
metatype: ExternalContextCreator,
|
||||
isResolved: true,
|
||||
instance: externalContextCreator,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addModulesContainer(modulesContainer: ModulesContainer) {
|
||||
this._providers.set(
|
||||
ModulesContainer.name,
|
||||
new InstanceWrapper({
|
||||
name: ModulesContainer.name,
|
||||
metatype: ModulesContainer,
|
||||
isResolved: true,
|
||||
instance: modulesContainer,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addApplicationRefHost(applicationRefHost: ApplicationReferenceHost) {
|
||||
this._providers.set(
|
||||
ApplicationReferenceHost.name,
|
||||
new InstanceWrapper({
|
||||
name: ApplicationReferenceHost.name,
|
||||
metatype: ApplicationReferenceHost,
|
||||
isResolved: true,
|
||||
instance: applicationRefHost,
|
||||
host: this,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public addInjectable<T extends Injectable>(
|
||||
injectable: Type<T>,
|
||||
host?: Type<T>,
|
||||
@@ -444,6 +367,10 @@ export class Module {
|
||||
});
|
||||
}
|
||||
|
||||
public getProviderByKey<T = any>(name: string | symbol): InstanceWrapper<T> {
|
||||
return this._providers.get(name) as InstanceWrapper<T>;
|
||||
}
|
||||
|
||||
public createModuleReferenceType(): any {
|
||||
const self = this;
|
||||
return class extends ModuleRef {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const HTTP_SERVER_REF = 'HTTP_SERVER_REF';
|
||||
@@ -23,6 +23,7 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): NestInterceptor[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(
|
||||
@@ -30,12 +31,14 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
callback,
|
||||
INTERCEPTORS_METADATA,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
}
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
@@ -45,7 +48,9 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
(interceptor: any) =>
|
||||
interceptor && (interceptor.name || interceptor.intercept),
|
||||
)
|
||||
.map(interceptor => this.getInterceptorInstance(interceptor, contextId))
|
||||
.map(interceptor =>
|
||||
this.getInterceptorInstance(interceptor, contextId, inquirerId),
|
||||
)
|
||||
.filter(
|
||||
(interceptor: NestInterceptor) =>
|
||||
interceptor && isFunction(interceptor.intercept),
|
||||
@@ -56,16 +61,20 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
public getInterceptorInstance(
|
||||
interceptor: Function | NestInterceptor,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
inquirerId?: string,
|
||||
): NestInterceptor | null {
|
||||
const isObject = (interceptor as NestInterceptor).intercept;
|
||||
if (isObject) {
|
||||
return interceptor;
|
||||
return interceptor as NestInterceptor;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(interceptor);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export class MiddlewareModule {
|
||||
config: ApplicationConfig,
|
||||
injector: Injector,
|
||||
) {
|
||||
const appRef = container.getApplicationRef();
|
||||
const appRef = container.getHttpAdapterRef();
|
||||
this.routerExceptionFilter = new RouterExceptionFilters(
|
||||
container,
|
||||
config,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { isNil, isUndefined } from '@nestjs/common/utils/shared.utils';
|
||||
import { InstanceWrapper } from 'injector/instance-wrapper';
|
||||
import iterate from 'iterare';
|
||||
import { UnknownModuleException } from './errors/exceptions/unknown-module.exception';
|
||||
import { NestContainer } from './injector/container';
|
||||
@@ -85,15 +86,25 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
// Lifecycle hook has to be called once all classes are properly initialized
|
||||
const [_, { instance: moduleClassInstance }] = providers.shift();
|
||||
const instances = [...module.controllers, ...providers];
|
||||
|
||||
await Promise.all(
|
||||
iterate(instances)
|
||||
.filter(([key, wrapper]) => wrapper.isDependencyTreeStatic())
|
||||
.map(([key, { instance }]) => instance)
|
||||
const callOperator = (list: any) =>
|
||||
list
|
||||
.filter(instance => !isNil(instance))
|
||||
.filter(this.hasOnModuleInitHook)
|
||||
.map(async instance => (instance as OnModuleInit).onModuleInit()),
|
||||
.map(async instance => (instance as OnModuleInit).onModuleInit());
|
||||
|
||||
await Promise.all(
|
||||
callOperator(
|
||||
iterate(instances)
|
||||
.filter(
|
||||
([key, wrapper]) =>
|
||||
wrapper.isDependencyTreeStatic() && !wrapper.isTransient,
|
||||
)
|
||||
.map(([key, { instance }]) => instance),
|
||||
),
|
||||
);
|
||||
const transientInstances = this.getTransientInstances(instances);
|
||||
await Promise.all(callOperator(iterate(transientInstances)));
|
||||
|
||||
if (moduleClassInstance && this.hasOnModuleInitHook(moduleClassInstance)) {
|
||||
await (moduleClassInstance as OnModuleInit).onModuleInit();
|
||||
}
|
||||
@@ -116,15 +127,25 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
// Lifecycle hook has to be called once all classes are properly destroyed
|
||||
const [_, { instance: moduleClassInstance }] = providers.shift();
|
||||
const instances = [...module.controllers, ...providers];
|
||||
|
||||
await Promise.all(
|
||||
iterate(instances)
|
||||
.filter(([key, wrapper]) => wrapper.isDependencyTreeStatic())
|
||||
.map(([key, { instance }]) => instance)
|
||||
const callOperator = (list: any) =>
|
||||
list
|
||||
.filter(instance => !isNil(instance))
|
||||
.filter(this.hasOnModuleDestroyHook)
|
||||
.map(async instance => (instance as OnModuleDestroy).onModuleDestroy()),
|
||||
.map(async instance => (instance as OnModuleDestroy).onModuleDestroy());
|
||||
|
||||
await Promise.all(
|
||||
callOperator(
|
||||
iterate(instances)
|
||||
.filter(
|
||||
([key, wrapper]) =>
|
||||
wrapper.isDependencyTreeStatic() && !wrapper.isTransient,
|
||||
)
|
||||
.map(([key, { instance }]) => instance),
|
||||
),
|
||||
);
|
||||
const transientInstances = this.getTransientInstances(instances);
|
||||
await Promise.all(callOperator(iterate(transientInstances)));
|
||||
|
||||
if (
|
||||
moduleClassInstance &&
|
||||
this.hasOnModuleDestroyHook(moduleClassInstance)
|
||||
@@ -149,16 +170,27 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
const [_, { instance: moduleClassInstance }] = providers.shift();
|
||||
const instances = [...module.controllers, ...providers];
|
||||
|
||||
await Promise.all(
|
||||
iterate(instances)
|
||||
.filter(([key, wrapper]) => wrapper.isDependencyTreeStatic())
|
||||
.map(([key, { instance }]) => instance)
|
||||
const callOperator = (list: any) =>
|
||||
list
|
||||
.filter(instance => !isNil(instance))
|
||||
.filter(this.hasOnAppBotstrapHook)
|
||||
.map(async instance =>
|
||||
(instance as OnApplicationBootstrap).onApplicationBootstrap(),
|
||||
),
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
callOperator(
|
||||
iterate(instances)
|
||||
.filter(
|
||||
([key, wrapper]) =>
|
||||
wrapper.isDependencyTreeStatic() && !wrapper.isTransient,
|
||||
)
|
||||
.map(([key, { instance }]) => instance),
|
||||
),
|
||||
);
|
||||
const transientInstances = this.getTransientInstances(instances);
|
||||
await Promise.all(callOperator(iterate(transientInstances)));
|
||||
|
||||
if (moduleClassInstance && this.hasOnAppBotstrapHook(moduleClassInstance)) {
|
||||
await (moduleClassInstance as OnApplicationBootstrap).onApplicationBootstrap();
|
||||
}
|
||||
@@ -187,4 +219,16 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
TResult
|
||||
>(metatypeOrToken, contextModule);
|
||||
}
|
||||
|
||||
private getTransientInstances(
|
||||
instances: [string, InstanceWrapper][],
|
||||
): InstanceWrapper[] {
|
||||
return iterate(instances)
|
||||
.filter(([key, wrapper]) => wrapper.isDependencyTreeStatic())
|
||||
.map(([key, wrapper]) => wrapper.getStaticTransientInstances())
|
||||
.flatten()
|
||||
.filter(item => !!item)
|
||||
.map(({ instance }: any) => instance)
|
||||
.toArray() as InstanceWrapper[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ export class NestFactoryStatic {
|
||||
'@nestjs/microservices',
|
||||
'NestFactory',
|
||||
);
|
||||
|
||||
const applicationConfig = new ApplicationConfig();
|
||||
const container = new NestContainer(applicationConfig);
|
||||
|
||||
this.applyLogger(options);
|
||||
|
||||
await this.initialize(module, container, applicationConfig);
|
||||
return this.createNestInstance<INestMicroservice>(
|
||||
new NestMicroservice(container, options, applicationConfig),
|
||||
@@ -129,7 +129,7 @@ export class NestFactoryStatic {
|
||||
new MetadataScanner(),
|
||||
config,
|
||||
);
|
||||
container.setApplicationRef(httpServer);
|
||||
container.setHttpAdapter(httpServer);
|
||||
try {
|
||||
this.logger.log(MESSAGES.APPLICATION_START);
|
||||
await ExceptionsZone.asyncRun(async () => {
|
||||
|
||||
@@ -27,21 +27,29 @@ export class PipesContextCreator extends ContextCreator {
|
||||
callback: (...args: any[]) => any,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): Transform<any>[] {
|
||||
this.moduleContext = module;
|
||||
return this.createContext(instance, callback, PIPES_METADATA, contextId);
|
||||
return this.createContext(
|
||||
instance,
|
||||
callback,
|
||||
PIPES_METADATA,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
}
|
||||
|
||||
public createConcreteContext<T extends any[], R extends any[]>(
|
||||
metadata: T,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): R {
|
||||
if (isEmpty(metadata)) {
|
||||
return [] as R;
|
||||
}
|
||||
return iterate(metadata)
|
||||
.filter((pipe: any) => pipe && (pipe.name || pipe.transform))
|
||||
.map(pipe => this.getPipeInstance(pipe, contextId))
|
||||
.map(pipe => this.getPipeInstance(pipe, contextId, inquirerId))
|
||||
.filter(pipe => pipe && pipe.transform && isFunction(pipe.transform))
|
||||
.map(pipe => pipe.transform.bind(pipe))
|
||||
.toArray() as R;
|
||||
@@ -50,16 +58,20 @@ export class PipesContextCreator extends ContextCreator {
|
||||
public getPipeInstance(
|
||||
pipe: Function | PipeTransform,
|
||||
contextId = STATIC_CONTEXT,
|
||||
) {
|
||||
inquirerId?: string,
|
||||
): PipeTransform | null {
|
||||
const isObject = (pipe as PipeTransform).transform;
|
||||
if (isObject) {
|
||||
return pipe;
|
||||
return pipe as PipeTransform;
|
||||
}
|
||||
const instanceWrapper = this.getInstanceByMetatype(pipe as Function);
|
||||
if (!instanceWrapper) {
|
||||
return null;
|
||||
}
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(contextId);
|
||||
const instanceHost = instanceWrapper.getInstanceByContextId(
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return instanceHost && instanceHost.instance;
|
||||
}
|
||||
|
||||
|
||||
1
packages/core/router/index.ts
Normal file
1
packages/core/router/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './request';
|
||||
@@ -8,5 +8,6 @@ export interface ExceptionsFilter {
|
||||
callback: Function,
|
||||
module: string,
|
||||
contextId?: ContextId,
|
||||
inquirerId?: string,
|
||||
): ExceptionsHandler;
|
||||
}
|
||||
|
||||
1
packages/core/router/request/index.ts
Normal file
1
packages/core/router/request/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './request-constants';
|
||||
1
packages/core/router/request/request-constants.ts
Normal file
1
packages/core/router/request/request-constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const REQUEST = Symbol('REQUEST');
|
||||
9
packages/core/router/request/request-providers.ts
Normal file
9
packages/core/router/request/request-providers.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Provider, Scope } from '@nestjs/common';
|
||||
import { REQUEST } from './request-constants';
|
||||
|
||||
const noop = () => {};
|
||||
export const requestProvider: Provider = {
|
||||
provide: REQUEST,
|
||||
scope: Scope.REQUEST,
|
||||
useFactory: noop,
|
||||
};
|
||||
@@ -23,6 +23,7 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
callback: RouterProxyCallback,
|
||||
module: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): ExceptionsHandler {
|
||||
this.moduleContext = module;
|
||||
|
||||
@@ -32,6 +33,7 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
callback,
|
||||
EXCEPTION_FILTERS_METADATA,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
if (isEmpty(filters)) {
|
||||
return exceptionHandler;
|
||||
|
||||
@@ -76,31 +76,38 @@ export class RouterExecutionContext {
|
||||
module: string,
|
||||
requestMethod: RequestMethod,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
) {
|
||||
const { argsLength, paramsOptions, fnHandleResponse } = this.getMetadata(
|
||||
instance,
|
||||
callback,
|
||||
methodName,
|
||||
module,
|
||||
requestMethod,
|
||||
const {
|
||||
argsLength,
|
||||
fnHandleResponse,
|
||||
paramtypes,
|
||||
getParamsMetadata,
|
||||
} = this.getMetadata(instance, callback, methodName, module, requestMethod);
|
||||
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
|
||||
getParamsMetadata(module, contextId, inquirerId),
|
||||
paramtypes,
|
||||
);
|
||||
const pipes = this.pipesContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const guards = this.guardsContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const interceptors = this.interceptorsContextCreator.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
|
||||
const fnCanActivate = this.createGuardsFn(guards, instance, callback);
|
||||
@@ -159,15 +166,24 @@ export class RouterExecutionContext {
|
||||
methodName,
|
||||
);
|
||||
const httpCode = this.reflectHttpStatusCode(callback);
|
||||
const paramsMetadata = this.exchangeKeysForValues(keys, metadata, module);
|
||||
const getParamsMetadata = (
|
||||
moduleKey: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
) =>
|
||||
this.exchangeKeysForValues(
|
||||
keys,
|
||||
metadata,
|
||||
moduleKey,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
|
||||
const paramsMetadata = getParamsMetadata(module);
|
||||
const isResponseHandled = paramsMetadata.some(
|
||||
({ type }) =>
|
||||
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
|
||||
);
|
||||
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
|
||||
paramsMetadata,
|
||||
paramtypes,
|
||||
);
|
||||
const httpStatusCode = httpCode
|
||||
? httpCode
|
||||
: this.responseController.getStatusByMethod(requestMethod);
|
||||
@@ -179,8 +195,9 @@ export class RouterExecutionContext {
|
||||
);
|
||||
const handlerMetadata: HandlerMetadata = {
|
||||
argsLength,
|
||||
paramsOptions,
|
||||
fnHandleResponse,
|
||||
paramtypes,
|
||||
getParamsMetadata,
|
||||
};
|
||||
this.handlerMetadataStorage.set(instance, methodName, handlerMetadata);
|
||||
return handlerMetadata;
|
||||
@@ -204,12 +221,16 @@ export class RouterExecutionContext {
|
||||
keys: string[],
|
||||
metadata: RouteParamsMetadata,
|
||||
moduleContext: string,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
): ParamProperties[] {
|
||||
this.pipesContextCreator.setModuleContext(moduleContext);
|
||||
return keys.map(key => {
|
||||
const { index, data, pipes: pipesCollection } = metadata[key];
|
||||
const pipes = this.pipesContextCreator.createConcreteContext(
|
||||
pipesCollection,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const type = this.contextUtils.mapParamType(key);
|
||||
|
||||
|
||||
@@ -9,18 +9,20 @@ import { ApplicationConfig } from '../application-config';
|
||||
import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception';
|
||||
import { GuardsConsumer } from '../guards/guards-consumer';
|
||||
import { GuardsContextCreator } from '../guards/guards-context-creator';
|
||||
import { createContextId } from '../helpers/context-id-factory';
|
||||
import { ROUTE_MAPPED_MESSAGE } from '../helpers/messages';
|
||||
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { Injector } from '../injector/injector';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
|
||||
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
|
||||
import { MetadataScanner } from '../metadata-scanner';
|
||||
import { PipesConsumer } from '../pipes/pipes-consumer';
|
||||
import { PipesContextCreator } from '../pipes/pipes-context-creator';
|
||||
import { ExceptionsFilter } from './interfaces/exceptions-filter.interface';
|
||||
import { REQUEST } from './request';
|
||||
import { RouteParamsFactory } from './route-params-factory';
|
||||
import { RouterExecutionContext } from './router-execution-context';
|
||||
import { RouterProxy, RouterProxyCallback } from './router-proxy';
|
||||
@@ -53,7 +55,7 @@ export class RouterExplorer {
|
||||
new GuardsConsumer(),
|
||||
new InterceptorsContextCreator(container, config),
|
||||
new InterceptorsConsumer(),
|
||||
container.getApplicationRef(),
|
||||
container.getHttpAdapterRef(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -176,7 +178,9 @@ export class RouterExplorer {
|
||||
res: TResponse,
|
||||
next: Function,
|
||||
) => {
|
||||
const contextId = { id: 1 }; // asyncId
|
||||
const contextId = createContextId();
|
||||
this.registerRequestProvider(req, contextId);
|
||||
|
||||
const contextInstance = await this.injector.loadPerContext(
|
||||
instance,
|
||||
module,
|
||||
@@ -190,6 +194,7 @@ export class RouterExplorer {
|
||||
moduleKey,
|
||||
requestMethod,
|
||||
contextId,
|
||||
instanceWrapper.id,
|
||||
)(req, res, next);
|
||||
},
|
||||
);
|
||||
@@ -212,6 +217,7 @@ export class RouterExplorer {
|
||||
module: string,
|
||||
requestMethod: RequestMethod,
|
||||
contextId = STATIC_CONTEXT,
|
||||
inquirerId?: string,
|
||||
) {
|
||||
const executionContext = this.executionContextCreator.create(
|
||||
instance,
|
||||
@@ -220,13 +226,25 @@ export class RouterExplorer {
|
||||
module,
|
||||
requestMethod,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
const exceptionFilter = this.exceptionsFilter.create(
|
||||
instance,
|
||||
callback,
|
||||
module,
|
||||
contextId,
|
||||
inquirerId,
|
||||
);
|
||||
return this.routerProxy.createProxy(executionContext, exceptionFilter);
|
||||
}
|
||||
|
||||
private registerRequestProvider<T = any>(request: T, contextId: ContextId) {
|
||||
const coreModuleRef = this.container.getInternalCoreModuleRef();
|
||||
const wrapper = coreModuleRef.getProviderByKey(REQUEST);
|
||||
|
||||
wrapper.setInstanceByContextId(contextId, {
|
||||
instance: request,
|
||||
isResolved: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class RoutesResolver implements Resolver {
|
||||
this.routerExceptionsFilter = new RouterExceptionFilters(
|
||||
container,
|
||||
config,
|
||||
container.getApplicationRef(),
|
||||
container.getHttpAdapterRef(),
|
||||
);
|
||||
this.routerBuilder = new RouterExplorer(
|
||||
new MetadataScanner(),
|
||||
@@ -73,7 +73,7 @@ export class RoutesResolver implements Resolver {
|
||||
}
|
||||
|
||||
public registerNotFoundHandler() {
|
||||
const applicationRef = this.container.getApplicationRef();
|
||||
const applicationRef = this.container.getHttpAdapterRef();
|
||||
const callback = <TRequest, TResponse>(req: TRequest, res: TResponse) => {
|
||||
const method = applicationRef.getRequestMethod(req);
|
||||
const url = applicationRef.getRequestUrl(req);
|
||||
@@ -100,7 +100,7 @@ export class RoutesResolver implements Resolver {
|
||||
undefined,
|
||||
);
|
||||
const proxy = this.routerProxy.createExceptionLayerProxy(callback, handler);
|
||||
const applicationRef = this.container.getApplicationRef();
|
||||
const applicationRef = this.container.getHttpAdapterRef();
|
||||
applicationRef.setErrorHandler && applicationRef.setErrorHandler(proxy);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { ApplicationConfig } from './application-config';
|
||||
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants';
|
||||
import { CircularDependencyException } from './errors/exceptions/circular-dependency.exception';
|
||||
import { NestContainer } from './injector/container';
|
||||
import { Module } from './injector/module';
|
||||
import { MetadataScanner } from './metadata-scanner';
|
||||
|
||||
interface ApplicationProviderWrapper {
|
||||
@@ -35,6 +36,7 @@ interface ApplicationProviderWrapper {
|
||||
|
||||
export class DependenciesScanner {
|
||||
private readonly applicationProvidersApplyMap: ApplicationProviderWrapper[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly container: NestContainer,
|
||||
private readonly metadataScanner: MetadataScanner,
|
||||
@@ -42,6 +44,7 @@ export class DependenciesScanner {
|
||||
) {}
|
||||
|
||||
public async scan(module: Type<any>) {
|
||||
await this.registerCoreModule();
|
||||
await this.scanForModules(module);
|
||||
await this.scanModulesForDependencies();
|
||||
this.container.bindGlobalScope();
|
||||
@@ -51,8 +54,8 @@ export class DependenciesScanner {
|
||||
module: ForwardReference | Type<any> | DynamicModule,
|
||||
scope: Type<any>[] = [],
|
||||
ctxRegistry: (ForwardReference | DynamicModule | Type<any>)[] = [],
|
||||
) {
|
||||
await this.insertModule(module, scope);
|
||||
): Promise<Module> {
|
||||
const moduleInstance = await this.insertModule(module, scope);
|
||||
ctxRegistry.push(module);
|
||||
|
||||
if (this.isForwardReference(module)) {
|
||||
@@ -78,13 +81,14 @@ export class DependenciesScanner {
|
||||
ctxRegistry,
|
||||
);
|
||||
}
|
||||
return moduleInstance;
|
||||
}
|
||||
|
||||
public async insertModule(module: any, scope: Type<any>[]) {
|
||||
public async insertModule(module: any, scope: Type<any>[]): Promise<Module> {
|
||||
if (module && module.forwardRef) {
|
||||
return this.container.addModule(module.forwardRef(), scope);
|
||||
}
|
||||
await this.container.addModule(module, scope);
|
||||
return this.container.addModule(module, scope);
|
||||
}
|
||||
|
||||
public async scanModulesForDependencies() {
|
||||
@@ -301,6 +305,12 @@ export class DependenciesScanner {
|
||||
return Reflect.getMetadata(metadataKey, metatype) || [];
|
||||
}
|
||||
|
||||
public async registerCoreModule() {
|
||||
const module = this.container.createCoreModule();
|
||||
const instance = await this.scanForModules(module);
|
||||
this.container.registerCoreModuleRef(instance);
|
||||
}
|
||||
|
||||
public applyApplicationProviders() {
|
||||
const applyProvidersMap = this.getApplyProvidersMap();
|
||||
this.applicationProvidersApplyMap.forEach(
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AbstractHttpAdapter } from '../../adapters';
|
||||
import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception';
|
||||
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||
import { ExecutionContextHost } from '../../helpers/execution-context-host';
|
||||
import { NoopHttpAdapter } from './../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from './../utils/noop-adapter.spec';
|
||||
|
||||
describe('ExceptionsHandler', () => {
|
||||
let adapter: AbstractHttpAdapter;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { expect } from 'chai';
|
||||
import { ApplicationReferenceHost } from './../../helpers/application-ref-host';
|
||||
import { HttpAdapterHost } from '../../helpers/http-adapter-host';
|
||||
|
||||
describe('ApplicationReferenceHost', () => {
|
||||
const applicationRefHost = new ApplicationReferenceHost();
|
||||
describe('HttpAdapterHost', () => {
|
||||
const applicationRefHost = new HttpAdapterHost();
|
||||
it('should wrap application reference', () => {
|
||||
const ref = {};
|
||||
applicationRefHost.applicationRef = ref;
|
||||
applicationRefHost.httpAdapter = ref;
|
||||
|
||||
expect(applicationRefHost.applicationRef).to.be.eql(ref);
|
||||
expect(applicationRefHost.httpAdapter).to.be.eql(ref);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Global } from '../../../common/index';
|
||||
import { CircularDependencyException } from '../../errors/exceptions/circular-dependency.exception';
|
||||
import { UnknownModuleException } from '../../errors/exceptions/unknown-module.exception';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { InternalCoreModule } from '../../injector/internal-core-module';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('NestContainer', () => {
|
||||
let container: NestContainer;
|
||||
@@ -173,4 +175,46 @@ describe('NestContainer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get applicationConfig', () => {
|
||||
it('should return ApplicationConfig instance', () => {
|
||||
expect(container.applicationConfig).to.be.eql(
|
||||
(container as any)._applicationConfig,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setHttpAdapter', () => {
|
||||
it('should set http adapter', () => {
|
||||
const httpAdapter = new NoopHttpAdapter({});
|
||||
container.setHttpAdapter(httpAdapter);
|
||||
|
||||
const internalStorage = (container as any).internalProvidersStorage;
|
||||
expect(internalStorage.httpAdapter).to.be.eql(httpAdapter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModuleByKey', () => {
|
||||
it('should return module by passed key', () => {
|
||||
const key = 'test';
|
||||
const value = {};
|
||||
container.getModules().set(key, value as any);
|
||||
|
||||
expect(container.getModuleByKey(key)).to.be.eql(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCoreModule', () => {
|
||||
it('should create InternalCoreModule', () => {
|
||||
expect(container.createCoreModule().module).to.be.eql(InternalCoreModule);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerCoreModuleRef', () => {
|
||||
it('should register core module ref', () => {
|
||||
const ref = {} as any;
|
||||
container.registerCoreModuleRef(ref);
|
||||
expect((container as any).internalCoreModule).to.be.eql(ref);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,25 +150,28 @@ describe('Injector', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null when collection is nil', () => {
|
||||
it('should return undefined when collection is nil', () => {
|
||||
const result = injector.loadPrototype(test, null);
|
||||
expect(result).to.be.null;
|
||||
expect(result).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should return null when target isResolved', () => {
|
||||
it('should return undefined when target isResolved', () => {
|
||||
const collection = {
|
||||
get: () => ({ getInstanceByContextId: () => ({ isResolved: true }) }),
|
||||
get: () => ({
|
||||
getInstanceByContextId: () => ({ isResolved: true }),
|
||||
createPrototype: () => {},
|
||||
}),
|
||||
};
|
||||
const result = injector.loadPrototype(test, collection as any);
|
||||
expect(result).to.be.null;
|
||||
expect(result).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should return null when "inject" is not nil', () => {
|
||||
it('should return undefined when "inject" is not nil', () => {
|
||||
const collection = {
|
||||
get: () => new InstanceWrapper({ inject: [] }),
|
||||
};
|
||||
const result = injector.loadPrototype(test, collection as any);
|
||||
expect(result).to.be.null;
|
||||
expect(result).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -236,7 +239,7 @@ describe('Injector', () => {
|
||||
|
||||
it('should call "loadInstance" with expected arguments', async () => {
|
||||
const module = { controllers: [] };
|
||||
const wrapper = { test: 'test' };
|
||||
const wrapper = { test: 'test', getEnhancersMetadata: () => [] };
|
||||
|
||||
await injector.loadController(wrapper as any, module as any);
|
||||
expect(loadInstance.calledWith(wrapper, module.controllers, module)).to.be
|
||||
@@ -605,4 +608,126 @@ describe('Injector', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('instantiateClass', () => {
|
||||
class TestClass {}
|
||||
|
||||
describe('when context is static', () => {
|
||||
it('should instantiate class', async () => {
|
||||
const wrapper = new InstanceWrapper({ metatype: TestClass });
|
||||
await injector.instantiateClass([], wrapper, wrapper, STATIC_CONTEXT);
|
||||
|
||||
expect(wrapper.instance).to.not.be.undefined;
|
||||
expect(wrapper.instance).to.be.instanceOf(TestClass);
|
||||
});
|
||||
it('should call factory', async () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
inject: [],
|
||||
metatype: (() => ({})) as any,
|
||||
});
|
||||
await injector.instantiateClass([], wrapper, wrapper, STATIC_CONTEXT);
|
||||
|
||||
expect(wrapper.instance).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
describe('when context is not static', () => {
|
||||
it('should not instantiate class', async () => {
|
||||
const ctx = { id: 3 };
|
||||
const wrapper = new InstanceWrapper({ metatype: TestClass });
|
||||
await injector.instantiateClass([], wrapper, wrapper, ctx);
|
||||
|
||||
expect(wrapper.instance).to.be.undefined;
|
||||
expect(wrapper.getInstanceByContextId(ctx).isResolved).to.be.true;
|
||||
});
|
||||
|
||||
it('should not call factory', async () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
inject: [],
|
||||
metatype: sinon.spy() as any,
|
||||
});
|
||||
await injector.instantiateClass([], wrapper, wrapper, { id: 2 });
|
||||
expect(wrapper.instance).to.be.undefined;
|
||||
expect((wrapper.metatype as any).called).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadPerContext', () => {
|
||||
class TestClass {}
|
||||
|
||||
it('should load instance per context id', async () => {
|
||||
const container = new NestContainer();
|
||||
const moduleCtor = class TestModule {};
|
||||
const ctx = STATIC_CONTEXT;
|
||||
const module = await container.addModule(moduleCtor, []);
|
||||
|
||||
module.addProvider({
|
||||
name: 'TestClass',
|
||||
provide: TestClass,
|
||||
useClass: TestClass,
|
||||
});
|
||||
const instance = await injector.loadPerContext(
|
||||
new TestClass(),
|
||||
module,
|
||||
module.providers,
|
||||
ctx,
|
||||
);
|
||||
expect(instance).to.be.instanceOf(TestClass);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadEnhancersPerContext', () => {
|
||||
it('should load enhancers per context id', async () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addEnhancerMetadata(new InstanceWrapper());
|
||||
wrapper.addEnhancerMetadata(new InstanceWrapper());
|
||||
|
||||
const loadInstanceStub = sinon
|
||||
.stub(injector, 'loadInstance')
|
||||
.callsFake(() => ({}));
|
||||
|
||||
await injector.loadEnhancersPerContext(
|
||||
wrapper,
|
||||
new Module(class {}, [], new NestContainer()),
|
||||
STATIC_CONTEXT,
|
||||
);
|
||||
expect(loadInstanceStub.calledTwice).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCtorMetadata', () => {
|
||||
it('should resolve ctor metadata', async () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addCtorMetadata(0, new InstanceWrapper());
|
||||
wrapper.addCtorMetadata(1, new InstanceWrapper());
|
||||
|
||||
const resolveComponentHostStub = sinon
|
||||
.stub(injector, 'resolveComponentHost')
|
||||
.callsFake(() => new InstanceWrapper());
|
||||
|
||||
await injector.loadCtorMetadata(
|
||||
wrapper.getCtorMetadata(),
|
||||
STATIC_CONTEXT,
|
||||
);
|
||||
expect(resolveComponentHostStub.calledTwice).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadPropertiesMetadata', () => {
|
||||
it('should resolve properties metadata', async () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addPropertiesMetadata('key1', new InstanceWrapper());
|
||||
wrapper.addPropertiesMetadata('key2', new InstanceWrapper());
|
||||
|
||||
const resolveComponentHostStub = sinon
|
||||
.stub(injector, 'resolveComponentHost')
|
||||
.callsFake(() => new InstanceWrapper());
|
||||
|
||||
await injector.loadPropertiesMetadata(
|
||||
wrapper.getPropertiesMetadata(),
|
||||
STATIC_CONTEXT,
|
||||
);
|
||||
expect(resolveComponentHostStub.calledTwice).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
325
packages/core/test/injector/instance-wrapper.spec.ts
Normal file
325
packages/core/test/injector/instance-wrapper.spec.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { STATIC_CONTEXT } from '../../injector/constants';
|
||||
import { InstanceWrapper } from '../../injector/instance-wrapper';
|
||||
|
||||
class TestClass {}
|
||||
|
||||
describe('InstanceWrapper', () => {
|
||||
describe('initialize', () => {
|
||||
const partial = {
|
||||
name: 'test',
|
||||
metatype: TestClass,
|
||||
scope: Scope.DEFAULT,
|
||||
instance: new TestClass(),
|
||||
};
|
||||
it('should assign partial', () => {
|
||||
const instance = new InstanceWrapper(partial);
|
||||
|
||||
expect(instance.name).to.be.eql(partial.name);
|
||||
expect(instance.scope).to.be.eql(partial.scope);
|
||||
expect(instance.metatype).to.be.eql(partial.metatype);
|
||||
});
|
||||
it('should set intance by context id', () => {
|
||||
const instance = new InstanceWrapper(partial);
|
||||
|
||||
expect(
|
||||
instance.getInstanceByContextId(STATIC_CONTEXT).instance,
|
||||
).to.be.eql(partial.instance);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDependencyTreeStatic', () => {
|
||||
describe('when request scoped', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
});
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('when statically scoped', () => {
|
||||
describe('dependencies', () => {
|
||||
describe('when each is static', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addCtorMetadata(0, new InstanceWrapper());
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('when one is not static', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addCtorMetadata(0, new InstanceWrapper());
|
||||
wrapper.addCtorMetadata(
|
||||
1,
|
||||
new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
}),
|
||||
);
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('properties', () => {
|
||||
describe('when each is static', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addPropertiesMetadata('key1', new InstanceWrapper());
|
||||
wrapper.addPropertiesMetadata('key2', new InstanceWrapper());
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('when one is not static', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addPropertiesMetadata(
|
||||
'key1',
|
||||
new InstanceWrapper({ scope: Scope.REQUEST }),
|
||||
);
|
||||
wrapper.addPropertiesMetadata('key2', new InstanceWrapper());
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('enhancers', () => {
|
||||
describe('when each is static', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addEnhancerMetadata(new InstanceWrapper());
|
||||
wrapper.addEnhancerMetadata(new InstanceWrapper());
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('when one is not static', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addEnhancerMetadata(
|
||||
new InstanceWrapper({ scope: Scope.REQUEST }),
|
||||
);
|
||||
wrapper.addEnhancerMetadata(new InstanceWrapper());
|
||||
expect(wrapper.isDependencyTreeStatic()).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNotMetatype', () => {
|
||||
describe('when metatype is nil', () => {
|
||||
it('should return true', () => {
|
||||
const instance = new InstanceWrapper({ metatype: null });
|
||||
expect(instance.isNotMetatype).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('when metatype is not nil', () => {
|
||||
it('should return false', () => {
|
||||
const instance = new InstanceWrapper({ metatype: TestClass });
|
||||
expect(instance.isNotMetatype).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addEnhancerMetadata', () => {
|
||||
it('should add enhancers metadata', () => {
|
||||
const instance = new InstanceWrapper();
|
||||
const enhancers = [new InstanceWrapper()];
|
||||
instance.addEnhancerMetadata(enhancers[0]);
|
||||
expect(instance.getEnhancersMetadata()).to.be.eql(enhancers);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when set instance has been called', () => {
|
||||
it('should set static context value', () => {
|
||||
const instance = { test: true };
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.instance = instance;
|
||||
|
||||
expect(wrapper.getInstanceByContextId(STATIC_CONTEXT).instance).to.be.eql(
|
||||
instance,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cloneStaticInstance', () => {
|
||||
describe('when wrapper is static', () => {
|
||||
it('should return static instance', () => {
|
||||
const instance = { test: true };
|
||||
const wrapper = new InstanceWrapper({ instance });
|
||||
|
||||
expect(wrapper.cloneStaticInstance({ id: 0 }).instance).to.be.eql(
|
||||
instance,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('when wrapper is not static', () => {
|
||||
it('should clone instance by context id', () => {
|
||||
const instance = { test: true };
|
||||
const wrapper = new InstanceWrapper({ instance, scope: Scope.REQUEST });
|
||||
|
||||
expect(wrapper.cloneStaticInstance({ id: 0 }).instance).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInstanceByContextId', () => {
|
||||
describe('when transient and inquirer has been passed', () => {
|
||||
it('should call "getInstanceByInquirerId"', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
const getInstanceByInquirerIdSpy = sinon.spy(
|
||||
wrapper,
|
||||
'getInstanceByInquirerId',
|
||||
);
|
||||
wrapper.getInstanceByContextId(STATIC_CONTEXT, 'inquirerId');
|
||||
expect(getInstanceByInquirerIdSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setInstanceByContextId', () => {
|
||||
describe('when transient and inquirer has been passed', () => {
|
||||
it('should call "setInstanceByInquirerId"', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
const setInstanceByInquirerIdSpy = sinon.spy(
|
||||
wrapper,
|
||||
'setInstanceByInquirerId',
|
||||
);
|
||||
wrapper.setInstanceByContextId(
|
||||
STATIC_CONTEXT,
|
||||
{ instance: {} },
|
||||
'inquirerId',
|
||||
);
|
||||
expect(setInstanceByInquirerIdSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInRequestScope', () => {
|
||||
describe('when tree and context are not static and is not transient', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
});
|
||||
expect(wrapper.isInRequestScope({ id: 3 })).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('otherwise', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
expect(wrapper.isInRequestScope({ id: 3 })).to.be.false;
|
||||
|
||||
const wrapper2 = new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
});
|
||||
expect(wrapper2.isInRequestScope(STATIC_CONTEXT)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLazyTransient', () => {
|
||||
describe('when inquirer is request scoped and context is not static and is transient', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
expect(
|
||||
wrapper.isLazyTransient(
|
||||
{ id: 3 },
|
||||
new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
}),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('otherwise', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
expect(wrapper.isLazyTransient({ id: 3 }, new InstanceWrapper())).to.be
|
||||
.false;
|
||||
|
||||
const wrapper2 = new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
});
|
||||
expect(
|
||||
wrapper2.isLazyTransient(
|
||||
STATIC_CONTEXT,
|
||||
new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
}),
|
||||
),
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isStatic', () => {
|
||||
describe('when inquirer is not request scoped and context and tree are static', () => {
|
||||
it('should return true', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.DEFAULT,
|
||||
});
|
||||
expect(
|
||||
wrapper.isStatic(
|
||||
STATIC_CONTEXT,
|
||||
new InstanceWrapper({
|
||||
scope: Scope.DEFAULT,
|
||||
}),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('otherwise', () => {
|
||||
it('should return false', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
});
|
||||
expect(wrapper.isStatic({ id: 3 }, new InstanceWrapper())).to.be.false;
|
||||
|
||||
const wrapper2 = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
expect(
|
||||
wrapper2.isStatic(
|
||||
STATIC_CONTEXT,
|
||||
new InstanceWrapper({
|
||||
scope: Scope.REQUEST,
|
||||
}),
|
||||
),
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStaticTransientInstances', () => {
|
||||
describe('when instance is not transient', () => {
|
||||
it('should return an empty array', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.DEFAULT,
|
||||
});
|
||||
expect(wrapper.getStaticTransientInstances()).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
describe('when instance is transient', () => {
|
||||
it('should return all static instances', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
const instanceHost = {
|
||||
instance: {},
|
||||
};
|
||||
wrapper.setInstanceByInquirerId(STATIC_CONTEXT, 'test', instanceHost);
|
||||
expect(wrapper.getStaticTransientInstances()).to.be.eql([instanceHost]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -277,10 +277,12 @@ describe('Module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('relatedModules', () => {
|
||||
describe('imports', () => {
|
||||
it('should return relatedModules', () => {
|
||||
const test = ['test'];
|
||||
(module as any)._imports = test;
|
||||
|
||||
expect(module.imports).to.be.eql(test);
|
||||
expect(module.relatedModules).to.be.eql(test);
|
||||
});
|
||||
});
|
||||
@@ -297,7 +299,9 @@ describe('Module', () => {
|
||||
it('should return controllers', () => {
|
||||
const test = ['test'];
|
||||
(module as any)._controllers = test;
|
||||
|
||||
expect(module.controllers).to.be.eql(test);
|
||||
expect(module.routes).to.be.eql(test);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -305,10 +309,21 @@ describe('Module', () => {
|
||||
it('should return exports', () => {
|
||||
const test = ['test'];
|
||||
(module as any)._exports = test;
|
||||
|
||||
expect(module.exports).to.be.eql(test);
|
||||
});
|
||||
});
|
||||
|
||||
describe('providers', () => {
|
||||
it('should return providers', () => {
|
||||
const test = ['test'];
|
||||
(module as any)._providers = test;
|
||||
|
||||
expect(module.providers).to.be.eql(test);
|
||||
expect(module.components).to.be.eql(test);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createModuleReferenceType', () => {
|
||||
let moduleRef;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { MiddlewareBuilder } from '../../middleware/builder';
|
||||
import { MiddlewareContainer } from '../../middleware/container';
|
||||
import { MiddlewareModule } from '../../middleware/middleware-module';
|
||||
import { RouterExceptionFilters } from '../../router/router-exception-filters';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('MiddlewareModule', () => {
|
||||
let middlewareModule: MiddlewareModule;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { UseFilters } from '../../../common/decorators/core/exception-filters.de
|
||||
import { ApplicationConfig } from '../../application-config';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { RouterExceptionFilters } from '../../router/router-exception-filters';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('RouterExceptionFilters', () => {
|
||||
let moduleName: string;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PipesConsumer } from '../../pipes/pipes-consumer';
|
||||
import { PipesContextCreator } from '../../pipes/pipes-context-creator';
|
||||
import { RouteParamsFactory } from '../../router/route-params-factory';
|
||||
import { RouterExecutionContext } from '../../router/router-execution-context';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('RouterExecutionContext', () => {
|
||||
let contextCreator: RouterExecutionContext;
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { RouterExplorer } from '../../router/router-explorer';
|
||||
import * as sinon from 'sinon';
|
||||
import { Controller } from '../../../common/decorators/core/controller.decorator';
|
||||
import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator';
|
||||
import {
|
||||
All,
|
||||
Get,
|
||||
Post,
|
||||
} from '../../../common/decorators/http/request-mapping.decorator';
|
||||
import { RequestMethod } from '../../../common/enums/request-method.enum';
|
||||
import { MetadataScanner } from '../../metadata-scanner';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { MetadataScanner } from '../../metadata-scanner';
|
||||
import { RouterExplorer } from '../../router/router-explorer';
|
||||
|
||||
describe('RouterExplorer', () => {
|
||||
@Controller('global')
|
||||
class TestRoute {
|
||||
@RequestMapping({ path: 'test' })
|
||||
@Get('test')
|
||||
public getTest() {}
|
||||
|
||||
@RequestMapping({ path: 'test', method: RequestMethod.POST })
|
||||
@Post('test')
|
||||
public postTest() {}
|
||||
|
||||
@RequestMapping({ path: 'another-test', method: RequestMethod.ALL })
|
||||
@All('another-test')
|
||||
public anotherTest() {}
|
||||
}
|
||||
|
||||
let routerBuilder: RouterExplorer;
|
||||
beforeEach(() => {
|
||||
routerBuilder = new RouterExplorer(new MetadataScanner(), new NestContainer());
|
||||
routerBuilder = new RouterExplorer(
|
||||
new MetadataScanner(),
|
||||
new NestContainer(),
|
||||
);
|
||||
});
|
||||
describe('scanForPaths', () => {
|
||||
it('should method return expected list of route paths', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as sinon from 'sinon';
|
||||
import { HttpException } from '../../../common/exceptions/http.exception';
|
||||
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||
import { RouterProxy } from '../../router/router-proxy';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('RouterProxy', () => {
|
||||
let routerProxy: RouterProxy;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
import { RequestMethod } from '../../../common';
|
||||
import { RouterResponseController } from '../../router/router-response-controller';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
|
||||
describe('RouterResponseController', () => {
|
||||
let adapter: NoopHttpAdapter;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user