From ef2299d1d6b4c4659997b8c88bd9690255cd9e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ribeiro?= Date: Thu, 5 Dec 2024 19:42:13 +0000 Subject: [PATCH] feat(core): adds ClsModule.registerPlugins to inject Plugins from an external module --- docs/docs/06_plugins/index.md | 6 ++ packages/core/src/lib/cls.module.ts | 13 ++++ packages/core/test/plugin/plugin.spec.ts | 76 ++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/docs/docs/06_plugins/index.md b/docs/docs/06_plugins/index.md index 83cf6254..9ff1dcde 100644 --- a/docs/docs/06_plugins/index.md +++ b/docs/docs/06_plugins/index.md @@ -18,6 +18,12 @@ ClsModule.forRoot({ }); ``` +If you need to inject Plugins from an external module, use the `ClsModule.registerPlugins()` registration to import the containing module. + +```ts +ClsModule.registerPlugins([new MyPlugin()]); +``` + ## Available plugins For a list of plugins managed by the author of `nestjs-cls`, see the [Available Plugins](./01_available-plugins/index.md) page. diff --git a/packages/core/src/lib/cls.module.ts b/packages/core/src/lib/cls.module.ts index ed104a68..2008f0f5 100644 --- a/packages/core/src/lib/cls.module.ts +++ b/packages/core/src/lib/cls.module.ts @@ -40,6 +40,7 @@ import { ClsPluginManager } from './plugin/cls-plugin-manager'; import { ProxyProviderManager } from './proxy-provider/proxy-provider-manager'; import { ClsModuleProxyProviderOptions } from './proxy-provider/proxy-provider.interfaces'; +import { ClsPlugin } from './plugin/cls-plugin.interface'; const clsServiceProvider: ValueProvider = { provide: ClsService, @@ -192,6 +193,18 @@ export class ClsModule implements NestModule { }; } + /** + * Registers the given Plugins the module along with `ClsService`. + */ + static registerPlugins(plugins: ClsPlugin[]): DynamicModule { + return { + module: ClsModule, + imports: ClsPluginManager.registerPlugins(plugins), + providers: commonProviders, + exports: commonProviders, + }; + } + private static createProxyClassProviders( proxyProviderClasses?: Array, ) { diff --git a/packages/core/test/plugin/plugin.spec.ts b/packages/core/test/plugin/plugin.spec.ts index 9fab6115..b33e76c7 100644 --- a/packages/core/test/plugin/plugin.spec.ts +++ b/packages/core/test/plugin/plugin.spec.ts @@ -4,6 +4,14 @@ import { NestFactory } from '@nestjs/core'; import { Controller, Get, Module } from '@nestjs/common'; import supertest from 'supertest'; +function providerToken(name: string) { + return `${name}ProviderToken`; +} + +function pluginInitializedToken(name: string) { + return `${name.toLocaleUpperCase()}_PLUGIN_INITIALIZED`; +} + function createDummyPlugin(name: string) { const watchers = { initHasRun: false, @@ -18,11 +26,11 @@ function createDummyPlugin(name: string) { watchers.destroyHasRun = true; }, onClsInit: (cls) => { - cls.set('PLUGIN_INITIALIZED', true); + cls.set(pluginInitializedToken(name), true); }, providers: [ { - provide: 'providerFromPlugin', + provide: providerToken(name), useValue: 'valueFromPlugin', }, ], @@ -51,7 +59,7 @@ describe('Plugins', () => { await module.init(); expect(watchers.initHasRun).toBe(true); - expect(module.get('providerFromPlugin')).toBe('valueFromPlugin'); + expect(module.get(providerToken('forRoot'))).toBe('valueFromPlugin'); expect(watchers.destroyHasRun).toBe(false); await module.close(); @@ -78,7 +86,9 @@ describe('Plugins', () => { await module.init(); expect(watchers.initHasRun).toBe(true); - expect(module.get('providerFromPlugin')).toBe('valueFromPlugin'); + expect(module.get(providerToken('forRootAsync'))).toBe( + 'valueFromPlugin', + ); expect(watchers.destroyHasRun).toBe(false); await module.close(); @@ -96,7 +106,7 @@ describe('Plugins', () => { constructor(private readonly cls: ClsService) {} @Get() get() { - return this.cls.get('PLUGIN_INITIALIZED'); + return this.cls.get(pluginInitializedToken('onClsInit')); } } @@ -123,4 +133,60 @@ describe('Plugins', () => { await module.close(); }, ); + + it('should register plugin and run module lifecycle and onClsInit methods (registerPlugins)', async () => { + const root = createDummyPlugin('root'); + const feature = createDummyPlugin('feature'); + + @Controller() + class TestController { + constructor(private readonly cls: ClsService) {} + @Get() + get() { + return ( + this.cls.get(pluginInitializedToken('root')) && + this.cls.get(pluginInitializedToken('feature')) + ); + } + } + @Module({ + imports: [ + ClsModule.forRoot({ + middleware: { + mount: true, + }, + plugins: [root.plugin], + }), + ClsModule.registerPlugins([feature.plugin]), + ], + controllers: [TestController], + }) + class TestAppModule {} + + const module = await NestFactory.create(TestAppModule); + + expect(root.watchers.initHasRun).toBe(false); + expect(feature.watchers.initHasRun).toBe(false); + + await module.init(); + + expect(root.watchers.initHasRun).toBe(true); + expect(feature.watchers.initHasRun).toBe(true); + + expect(module.get(providerToken('root'))).toBe('valueFromPlugin'); + expect(module.get(providerToken('feature'))).toBe('valueFromPlugin'); + + expect(root.watchers.destroyHasRun).toBe(false); + expect(feature.watchers.destroyHasRun).toBe(false); + + await supertest(module.getHttpServer()) + .get('/') + .expect(200) + .expect('true'); + + await module.close(); + + expect(root.watchers.destroyHasRun).toBe(true); + expect(feature.watchers.destroyHasRun).toBe(true); + }); });