diff --git a/packages/core/e2e/fixtures/test-plugins/with-global-providers.ts b/packages/core/e2e/fixtures/test-plugins/with-global-providers.ts new file mode 100644 index 0000000000..45cf0ee3c0 --- /dev/null +++ b/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 { + return next.handle(); + } +} + +@Injectable() +export class TestPipe implements PipeTransform { + 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(); + const request = ctx.getRequest(); + 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 {} diff --git a/packages/core/e2e/plugin.e2e-spec.ts b/packages/core/e2e/plugin.e2e-spec.ts index d8ecc88848..825f735bdf 100644 --- a/packages/core/e2e/plugin.e2e-spec.ts +++ b/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, ], }); diff --git a/packages/core/src/plugin/vendure-plugin.ts b/packages/core/src/plugin/vendure-plugin.ts index ba9e2290b3..5eca58eb99 100644 --- a/packages/core/src/plugin/vendure-plugin.ts +++ b/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> { + return provider.hasOwnProperty('provide'); +}