فهرست منبع

fix(core): Allow plugins to define global Nestjs providers

Fixes #837
Michael Bromley 4 سال پیش
والد
کامیت
97edcb9a29

+ 80 - 0
packages/core/e2e/fixtures/test-plugins/with-global-providers.ts

@@ -0,0 +1,80 @@
+import {
+    ArgumentMetadata,
+    ArgumentsHost,
+    CallHandler,
+    CanActivate,
+    Catch,
+    ExceptionFilter,
+    ExecutionContext,
+    HttpException,
+    Injectable,
+    NestInterceptor,
+    PipeTransform,
+} from '@nestjs/common';
+import { APP_FILTER, APP_GUARD } from '@nestjs/core';
+import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
+import { VendurePlugin } from '@vendure/core';
+import { Observable } from 'rxjs';
+
+@Injectable()
+export class TestInterceptor implements NestInterceptor {
+    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
+        return next.handle();
+    }
+}
+
+@Injectable()
+export class TestPipe implements PipeTransform<any> {
+    async transform(value: any, { metatype }: ArgumentMetadata) {
+        return value;
+    }
+}
+
+@Injectable()
+export class TestGuard implements CanActivate {
+    canActivate(context: ExecutionContext) {
+        return true;
+    }
+}
+
+@Catch(HttpException)
+export class HttpExceptionFilter implements ExceptionFilter {
+    catch(exception: HttpException, host: ArgumentsHost) {
+        const ctx = host.switchToHttp();
+        const response = ctx.getResponse<any>();
+        const request = ctx.getRequest<any>();
+        const status = exception.getStatus();
+
+        response.status(status).json({
+            statusCode: status,
+            timestamp: new Date().toISOString(),
+            path: request.url,
+        });
+    }
+}
+
+/**
+ * This plugin doesn't do anything other than attempt to register the global Nest providers
+ * in order to test https://github.com/vendure-ecommerce/vendure/issues/837
+ */
+@VendurePlugin({
+    providers: [
+        {
+            provide: APP_INTERCEPTOR,
+            useClass: TestInterceptor,
+        },
+        {
+            provide: APP_PIPE,
+            useClass: TestPipe,
+        },
+        {
+            provide: APP_GUARD,
+            useClass: TestGuard,
+        },
+        {
+            provide: APP_FILTER,
+            useClass: HttpExceptionFilter,
+        },
+    ],
+})
+export class PluginWithGlobalProviders {}

+ 2 - 0
packages/core/e2e/plugin.e2e-spec.ts

@@ -10,6 +10,7 @@ import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-conf
 import { TestPluginWithAllLifecycleHooks } from './fixtures/test-plugins/with-all-lifecycle-hooks';
 import { TestAPIExtensionPlugin } from './fixtures/test-plugins/with-api-extensions';
 import { TestPluginWithConfig } from './fixtures/test-plugins/with-config';
+import { PluginWithGlobalProviders } from './fixtures/test-plugins/with-global-providers';
 import { TestLazyExtensionPlugin } from './fixtures/test-plugins/with-lazy-api-extensions';
 import { TestPluginWithProvider } from './fixtures/test-plugins/with-provider';
 import { TestRestPlugin } from './fixtures/test-plugins/with-rest-controller';
@@ -25,6 +26,7 @@ describe('Plugins', () => {
             TestPluginWithProvider,
             TestLazyExtensionPlugin,
             TestRestPlugin,
+            PluginWithGlobalProviders,
         ],
     });
 

+ 17 - 5
packages/core/src/plugin/vendure-plugin.ts

@@ -1,6 +1,7 @@
-import { Module } from '@nestjs/common';
+import { Module, Provider, Type as NestType } from '@nestjs/common';
 import { MODULE_METADATA } from '@nestjs/common/constants';
 import { ModuleMetadata } from '@nestjs/common/interfaces';
+import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
 import { pick } from '@vendure/common/lib/pick';
 import { Type } from '@vendure/common/lib/shared-types';
 import { DocumentNode } from 'graphql';
@@ -134,10 +135,21 @@ export function VendurePlugin(pluginMetadata: VendurePluginMetadata): ClassDecor
         // created a new Module in the ApiModule, and if those resolvers depend on any providers,
         // the must be exported. See the function {@link createDynamicGraphQlModulesForPlugins}
         // for the implementation.
-        nestModuleMetadata.exports = [
-            ...(nestModuleMetadata.exports || []),
-            ...(nestModuleMetadata.providers || []),
-        ];
+        // However, we must omit any global providers (https://github.com/vendure-ecommerce/vendure/issues/837)
+        const nestGlobalProviderTokens = [APP_INTERCEPTOR, APP_FILTER, APP_GUARD, APP_PIPE];
+        const exportedProviders = (nestModuleMetadata.providers || []).filter(provider => {
+            if (isNamedProvider(provider)) {
+                if (nestGlobalProviderTokens.includes(provider.provide as any)) {
+                    return false;
+                }
+            }
+            return true;
+        });
+        nestModuleMetadata.exports = [...(nestModuleMetadata.exports || []), ...exportedProviders];
         Module(nestModuleMetadata)(target);
     };
 }
+
+function isNamedProvider(provider: Provider): provider is Exclude<Provider, NestType<any>> {
+    return provider.hasOwnProperty('provide');
+}