Skip to content

Commit

Permalink
fix(core): Allow plugins to define global Nestjs providers
Browse files Browse the repository at this point in the history
Fixes #837
  • Loading branch information
michaelbromley committed Apr 22, 2021
1 parent fad9006 commit 97edcb9
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 5 deletions.
80 changes: 80 additions & 0 deletions packages/core/e2e/fixtures/test-plugins/with-global-providers.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions packages/core/e2e/plugin.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,6 +26,7 @@ describe('Plugins', () => {
TestPluginWithProvider,
TestLazyExtensionPlugin,
TestRestPlugin,
PluginWithGlobalProviders,
],
});

Expand Down
22 changes: 17 additions & 5 deletions packages/core/src/plugin/vendure-plugin.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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');
}

0 comments on commit 97edcb9

Please sign in to comment.