Skip to content

Commit 539e281

Browse files
Merge pull request nestjs#3889 from nestjs/fix/lifecycle-hooks-3870
fix(core): fix lifecycle hooks for middleware, injectables
2 parents 7dbaf27 + 1004647 commit 539e281

11 files changed

Lines changed: 89 additions & 40 deletions

File tree

integration/docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ services:
4848
zookeeper:
4949
container_name: test-zookeeper
5050
hostname: zookeeper
51-
image: confluentinc/cp-zookeeper:5.4.0
51+
image: confluentinc/cp-zookeeper:5.3.2
5252
ports:
5353
- "2181:2181"
5454
environment:
@@ -57,7 +57,7 @@ services:
5757
kafka:
5858
container_name: test-kafka
5959
hostname: kafka
60-
image: confluentinc/cp-kafka:5.4.0
60+
image: confluentinc/cp-kafka:5.3.2
6161
depends_on:
6262
- zookeeper
6363
ports:

packages/core/hooks/on-app-bootstrap.hook.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ export async function callModuleBootstrapHook(module: Module): Promise<any> {
4343
// Module (class) instance is the first element of the providers array
4444
// Lifecycle hook has to be called once all classes are properly initialized
4545
const [_, { instance: moduleClassInstance }] = providers.shift();
46-
const instances = [...module.controllers, ...providers];
46+
const instances = [
47+
...module.controllers,
48+
...providers,
49+
...module.injectables,
50+
...module.middlewares,
51+
];
4752

4853
const nonTransientInstances = getNonTransientInstances(instances);
4954
await Promise.all(callOperator(nonTransientInstances));

packages/core/hooks/on-app-shutdown.hook.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ export async function callAppShutdownHook(
5151
// Module (class) instance is the first element of the providers array
5252
// Lifecycle hook has to be called once all classes are properly initialized
5353
const [_, { instance: moduleClassInstance }] = providers.shift();
54-
const instances = [...module.controllers, ...providers];
54+
const instances = [
55+
...module.controllers,
56+
...providers,
57+
...module.injectables,
58+
...module.middlewares,
59+
];
5560

5661
const nonTransientInstances = getNonTransientInstances(instances);
5762
await Promise.all(callOperator(nonTransientInstances, signal));

packages/core/hooks/on-module-destroy.hook.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ export async function callModuleDestroyHook(module: Module): Promise<any> {
4343
// Module (class) instance is the first element of the providers array
4444
// Lifecycle hook has to be called once all classes are properly destroyed
4545
const [_, { instance: moduleClassInstance }] = providers.shift();
46-
const instances = [...module.controllers, ...providers];
46+
const instances = [
47+
...module.controllers,
48+
...providers,
49+
...module.injectables,
50+
...module.middlewares,
51+
];
4752

4853
const nonTransientInstances = getNonTransientInstances(instances);
4954
await Promise.all(callOperator(nonTransientInstances));

packages/core/hooks/on-module-init.hook.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ export async function callModuleInitHook(module: Module): Promise<void> {
3939
// Module (class) instance is the first element of the providers array
4040
// Lifecycle hook has to be called once all classes are properly initialized
4141
const [_, { instance: moduleClassInstance }] = providers.shift();
42-
const instances = [...module.controllers, ...providers];
42+
const instances = [
43+
...module.controllers,
44+
...providers,
45+
...module.injectables,
46+
...module.middlewares,
47+
];
4348

4449
const nonTransientInstances = getNonTransientInstances(instances);
4550
await Promise.all(callOperator(nonTransientInstances));

packages/core/injector/module.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class Module {
3939
private readonly _imports = new Set<Module>();
4040
private readonly _providers = new Map<any, InstanceWrapper<Injectable>>();
4141
private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>();
42+
private readonly _middlewares = new Map<any, InstanceWrapper<Injectable>>();
4243
private readonly _controllers = new Map<
4344
string,
4445
InstanceWrapper<Controller>
@@ -51,7 +52,7 @@ export class Module {
5152
private readonly _scope: Type<any>[],
5253
private readonly container: NestContainer,
5354
) {
54-
this.addCoreProviders(container);
55+
this.addCoreProviders();
5556
this._id = randomStringGenerator();
5657
}
5758

@@ -67,6 +68,10 @@ export class Module {
6768
return this._providers;
6869
}
6970

71+
get middlewares(): Map<any, InstanceWrapper<Injectable>> {
72+
return this._middlewares;
73+
}
74+
7075
get imports(): Set<Module> {
7176
return this._imports;
7277
}
@@ -124,7 +129,7 @@ export class Module {
124129
this._distance = value;
125130
}
126131

127-
public addCoreProviders(container: NestContainer) {
132+
public addCoreProviders() {
128133
this.addModuleAsProvider();
129134
this.addModuleRef();
130135
this.addApplicationConfig();

packages/core/middleware/container.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Scope, Type } from '@nestjs/common';
22
import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';
33
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
4+
import { NestContainer } from '../injector';
45
import { InstanceWrapper } from '../injector/instance-wrapper';
56

67
export class MiddlewareContainer {
@@ -10,17 +11,28 @@ export class MiddlewareContainer {
1011
Set<MiddlewareConfiguration>
1112
>();
1213

13-
public getMiddlewareCollection(module: string): Map<string, InstanceWrapper> {
14-
return this.middleware.get(module) || new Map();
14+
constructor(private readonly container: NestContainer) {}
15+
16+
public getMiddlewareCollection(
17+
moduleKey: string,
18+
): Map<string, InstanceWrapper> {
19+
if (!this.middleware.has(moduleKey)) {
20+
const moduleRef = this.container.getModuleByKey(moduleKey);
21+
this.middleware.set(moduleKey, moduleRef.middlewares);
22+
}
23+
return this.middleware.get(moduleKey);
1524
}
1625

1726
public getConfigurations(): Map<string, Set<MiddlewareConfiguration>> {
1827
return this.configurationSets;
1928
}
2029

21-
public insertConfig(configList: MiddlewareConfiguration[], module: string) {
22-
const middleware = this.getTargetMiddleware(module);
23-
const targetConfig = this.getTargetConfig(module);
30+
public insertConfig(
31+
configList: MiddlewareConfiguration[],
32+
moduleKey: string,
33+
) {
34+
const middleware = this.getMiddlewareCollection(moduleKey);
35+
const targetConfig = this.getTargetConfig(moduleKey);
2436

2537
const configurations = configList || [];
2638
const insertMiddleware = <T extends Type<any>>(metatype: T) => {
@@ -40,13 +52,6 @@ export class MiddlewareContainer {
4052
});
4153
}
4254

43-
private getTargetMiddleware(module: string) {
44-
if (!this.middleware.has(module)) {
45-
this.middleware.set(module, new Map<string, InstanceWrapper>());
46-
}
47-
return this.middleware.get(module);
48-
}
49-
5055
private getTargetConfig(module: string) {
5156
if (!this.configurationSets.has(module)) {
5257
this.configurationSets.set(module, new Set<MiddlewareConfiguration>());

packages/core/nest-application.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ export class NestApplication extends NestApplicationContext
4444
implements INestApplication {
4545
private readonly logger = new Logger(NestApplication.name, true);
4646
private readonly middlewareModule = new MiddlewareModule();
47-
private readonly middlewareContainer = new MiddlewareContainer();
48-
private readonly microservicesModule = MicroservicesModule
49-
? new MicroservicesModule()
50-
: null;
51-
private readonly socketModule = SocketModule ? new SocketModule() : null;
47+
private readonly middlewareContainer = new MiddlewareContainer(
48+
this.container,
49+
);
50+
private readonly microservicesModule =
51+
MicroservicesModule && new MicroservicesModule();
52+
private readonly socketModule = SocketModule && new SocketModule();
5253
private readonly routesResolver: Resolver;
5354
private readonly microservices: any[] = [];
5455
private httpServer: any;

packages/core/test/middleware/container.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import { RequestMapping } from '../../../common/decorators/http/request-mapping.
55
import { RequestMethod } from '../../../common/enums/request-method.enum';
66
import { MiddlewareConfiguration } from '../../../common/interfaces/middleware/middleware-configuration.interface';
77
import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface';
8+
import { NestContainer } from '../../injector';
89
import { InstanceWrapper } from '../../injector/instance-wrapper';
10+
import { Module } from '../../injector/module';
911
import { MiddlewareContainer } from '../../middleware/container';
1012

1113
describe('MiddlewareContainer', () => {
14+
class ExampleModule {}
15+
1216
@Controller('test')
1317
class TestRoute {
1418
@RequestMapping({ path: 'test' })
@@ -26,7 +30,13 @@ describe('MiddlewareContainer', () => {
2630
let container: MiddlewareContainer;
2731

2832
beforeEach(() => {
29-
container = new MiddlewareContainer();
33+
const nestContainer = new NestContainer();
34+
const modules = nestContainer.getModules();
35+
36+
modules.set('Module', new Module(ExampleModule, [], nestContainer));
37+
modules.set('Test', new Module(ExampleModule, [], nestContainer));
38+
39+
container = new MiddlewareContainer(nestContainer);
3040
});
3141

3242
it('should store expected configurations for given module', () => {
@@ -36,7 +46,7 @@ describe('MiddlewareContainer', () => {
3646
forRoutes: [TestRoute, 'test'],
3747
},
3848
];
39-
container.insertConfig(config, 'Module' as any);
49+
container.insertConfig(config, 'Module');
4050
expect([...container.getConfigurations().get('Module')]).to.deep.equal(
4151
config,
4252
);
@@ -50,7 +60,7 @@ describe('MiddlewareContainer', () => {
5060
},
5161
];
5262

53-
const key = 'Test' as any;
63+
const key = 'Test';
5464
container.insertConfig(config, key);
5565

5666
const collection = container.getMiddlewareCollection(key);

packages/core/test/middleware/middlewares-module.spec.ts renamed to packages/core/test/middleware/middleware-module.spec.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('MiddlewareModule', () => {
5656
};
5757

5858
await middlewareModule.loadConfiguration(
59-
new MiddlewareContainer(),
59+
new MiddlewareContainer(new NestContainer()),
6060
mockModule as any,
6161
'Test' as any,
6262
);
@@ -71,27 +71,35 @@ describe('MiddlewareModule', () => {
7171
});
7272

7373
describe('registerRouteMiddleware', () => {
74+
class TestModule {}
75+
76+
let nestContainer: NestContainer;
77+
78+
beforeEach(() => {
79+
nestContainer = new NestContainer();
80+
nestContainer
81+
.getModules()
82+
.set('Test', new Module(TestModule, [], nestContainer));
83+
});
7484
it('should throw "RuntimeException" exception when middleware is not stored in container', () => {
7585
const route = { path: 'Test' };
7686
const configuration = {
7787
middleware: [TestMiddleware],
7888
forRoutes: [BaseController],
7989
};
80-
8190
const useSpy = sinon.spy();
8291
const app = { use: useSpy };
8392

84-
const nestContainer = new NestContainer();
8593
// tslint:disable-next-line:no-string-literal
8694
middlewareModule['container'] = nestContainer;
8795

8896
expect(
8997
middlewareModule.registerRouteMiddleware(
90-
new MiddlewareContainer(),
98+
new MiddlewareContainer(nestContainer),
9199
route as any,
92100
configuration,
93-
'Test' as any,
94-
app as any,
101+
'Test',
102+
app,
95103
),
96104
).to.eventually.be.rejectedWith(RuntimeException);
97105
});
@@ -109,8 +117,8 @@ describe('MiddlewareModule', () => {
109117
const useSpy = sinon.spy();
110118
const app = { use: useSpy };
111119

112-
const container = new MiddlewareContainer();
113-
const moduleKey = 'Test' as any;
120+
const container = new MiddlewareContainer(nestContainer);
121+
const moduleKey = 'Test';
114122
container.insertConfig([configuration], moduleKey);
115123

116124
const instance = new InvalidMiddleware();
@@ -125,7 +133,7 @@ describe('MiddlewareModule', () => {
125133
route as any,
126134
configuration,
127135
moduleKey,
128-
app as any,
136+
app,
129137
),
130138
).to.be.rejectedWith(InvalidMiddlewareException);
131139
});
@@ -143,7 +151,7 @@ describe('MiddlewareModule', () => {
143151
const app = {
144152
createMiddlewareFactory: createMiddlewareFactoryStub,
145153
};
146-
const container = new MiddlewareContainer();
154+
const container = new MiddlewareContainer(new NestContainer());
147155
const moduleKey = 'Test';
148156
container.insertConfig([configuration], moduleKey);
149157

@@ -155,7 +163,6 @@ describe('MiddlewareModule', () => {
155163
instance,
156164
}),
157165
);
158-
const nestContainer = new NestContainer();
159166
sinon
160167
.stub(nestContainer, 'getModuleByKey')
161168
.callsFake(() => new Module(class {}, [], nestContainer));

0 commit comments

Comments
 (0)