diff --git a/.vscode/settings.json b/.vscode/settings.json index 45f29296e..a28ad889f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { "editor.formatOnSave": true, - "cSpell.words": ["rabbitmq", "Vitest"], + "cSpell.words": ["Hasura", "rabbitmq", "Vitest"], "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/jest-e2e.json b/jest-e2e.json index 1747025c9..1674a4d4a 100644 --- a/jest-e2e.json +++ b/jest-e2e.json @@ -14,7 +14,6 @@ }, "moduleNameMapper": { "^@golevelup/nestjs-rabbitmq$": "/packages/rabbitmq/src", - "^@golevelup/nestjs-discovery$": "/packages/discovery/src", - "^@golevelup/nestjs-modules$": "/packages/modules/src" + "^@golevelup/nestjs-discovery$": "/packages/discovery/src" } } diff --git a/packages/hasura/README.md b/packages/hasura/README.md index 57034165f..b23539a75 100644 --- a/packages/hasura/README.md +++ b/packages/hasura/README.md @@ -76,7 +76,7 @@ import { HasuraModule } from '@golevelup/nestjs-hasura'; @Module({ imports: [ - HasuraModule.forRoot(HasuraModule, { + HasuraModule.forRoot({ webhookConfig: { secretFactory: secret, secretHeader: secretHeader, diff --git a/packages/hasura/package.json b/packages/hasura/package.json index 28e6f15ee..2c48fec7a 100644 --- a/packages/hasura/package.json +++ b/packages/hasura/package.json @@ -36,9 +36,7 @@ "access": "public" }, "dependencies": { - "@golevelup/nestjs-common": "workspace:^", "@golevelup/nestjs-discovery": "workspace:^", - "@golevelup/nestjs-modules": "workspace:^", "@hasura/metadata": "^1.0.2", "js-yaml": "^4.1.0", "zod": "^3.24.1" diff --git a/packages/hasura/src/hasura-module-definition.ts b/packages/hasura/src/hasura-module-definition.ts new file mode 100644 index 000000000..cf757cf81 --- /dev/null +++ b/packages/hasura/src/hasura-module-definition.ts @@ -0,0 +1,7 @@ +import { ConfigurableModuleBuilder } from '@nestjs/common'; +import { HasuraModuleConfig } from './hasura.interfaces'; + +export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = + new ConfigurableModuleBuilder() + .setClassMethodName('forRoot') + .build(); diff --git a/packages/hasura/src/hasura.constants.ts b/packages/hasura/src/hasura.constants.ts index 621c23b53..d9e894db6 100644 --- a/packages/hasura/src/hasura.constants.ts +++ b/packages/hasura/src/hasura.constants.ts @@ -1,5 +1,4 @@ export const HASURA_EVENT_HANDLER = Symbol('HASURA_EVENT_HANDLER'); -export const HASURA_MODULE_CONFIG = Symbol('HASURA_MODULE_CONFIG'); export const HASURA_SCHEDULED_EVENT_HANDLER = Symbol( - 'HASURA_SCHEDULED_EVENT_HANDLER' + 'HASURA_SCHEDULED_EVENT_HANDLER', ); diff --git a/packages/hasura/src/hasura.decorators.ts b/packages/hasura/src/hasura.decorators.ts index 3142dabd0..2d62ddd85 100644 --- a/packages/hasura/src/hasura.decorators.ts +++ b/packages/hasura/src/hasura.decorators.ts @@ -1,8 +1,7 @@ -import { makeInjectableDecorator } from '@golevelup/nestjs-common'; -import { applyDecorators, SetMetadata } from '@nestjs/common'; +import { applyDecorators, Inject, SetMetadata } from '@nestjs/common'; +import { MODULE_OPTIONS_TOKEN } from './hasura-module-definition'; import { HASURA_EVENT_HANDLER, - HASURA_MODULE_CONFIG, HASURA_SCHEDULED_EVENT_HANDLER, } from './hasura.constants'; import { @@ -14,16 +13,16 @@ import { export const HasuraEventHandler = (config: HasuraEventHandlerConfig) => applyDecorators(SetMetadata(HASURA_EVENT_HANDLER, config)); -export const InjectHasuraConfig = makeInjectableDecorator(HASURA_MODULE_CONFIG); +export const InjectHasuraConfig = () => Inject(MODULE_OPTIONS_TOKEN); export const TrackedHasuraEventHandler = ( - config: TrackedHasuraEventHandlerConfig + config: TrackedHasuraEventHandlerConfig, ) => applyDecorators(SetMetadata(HASURA_EVENT_HANDLER, config)); export const TrackedHasuraScheduledEventHandler = ( - config: TrackedHasuraScheduledEventHandlerConfig + config: TrackedHasuraScheduledEventHandlerConfig, ) => applyDecorators( SetMetadata(HASURA_SCHEDULED_EVENT_HANDLER, config), - SetMetadata(HASURA_EVENT_HANDLER, { triggerName: config.name }) + SetMetadata(HASURA_EVENT_HANDLER, { triggerName: config.name }), ); diff --git a/packages/hasura/src/hasura.event-handler.guard.ts b/packages/hasura/src/hasura.event-handler.guard.ts index 986ad4123..3a5a530f2 100644 --- a/packages/hasura/src/hasura.event-handler.guard.ts +++ b/packages/hasura/src/hasura.event-handler.guard.ts @@ -1,5 +1,5 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { Request } from 'express'; +import type { Request } from 'express'; import { Observable } from 'rxjs'; import { InjectHasuraConfig } from './hasura.decorators'; import { HasuraModuleConfig } from './hasura.interfaces'; @@ -9,7 +9,7 @@ export class HasuraEventHandlerHeaderGuard implements CanActivate { private readonly apiSecret: string; constructor( @InjectHasuraConfig() - private readonly hasuraConfig: HasuraModuleConfig + private readonly hasuraConfig: HasuraModuleConfig, ) { this.apiSecret = typeof hasuraConfig.webhookConfig.secretFactory === 'function' @@ -18,7 +18,7 @@ export class HasuraEventHandlerHeaderGuard implements CanActivate { } canActivate( - context: ExecutionContext + context: ExecutionContext, ): boolean | Promise | Observable { const request = context.switchToHttp().getRequest(); diff --git a/packages/hasura/src/hasura.event-handler.service.ts b/packages/hasura/src/hasura.event-handler.service.ts index 4f6ff8c76..8c0b84fea 100644 --- a/packages/hasura/src/hasura.event-handler.service.ts +++ b/packages/hasura/src/hasura.event-handler.service.ts @@ -4,7 +4,7 @@ import { HasuraEvent } from './hasura.interfaces'; @Injectable() export class EventHandlerService { public handleEvent(evt: HasuraEvent): any { - // The implementation for this method is overriden by the containing module + // The implementation for this method is overridden by the containing module console.log(evt); } } diff --git a/packages/hasura/src/hasura.module.ts b/packages/hasura/src/hasura.module.ts index 4c99dd9fd..5b1934632 100644 --- a/packages/hasura/src/hasura.module.ts +++ b/packages/hasura/src/hasura.module.ts @@ -1,5 +1,4 @@ import { DiscoveryModule, DiscoveryService } from '@golevelup/nestjs-discovery'; -import { createConfigurableDynamicRootModule } from '@golevelup/nestjs-modules'; import { BadRequestException, Logger, @@ -9,9 +8,12 @@ import { import { PATH_METADATA } from '@nestjs/common/constants'; import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator'; import { flatten, groupBy } from 'lodash'; +import { + ConfigurableModuleClass, + MODULE_OPTIONS_TOKEN, +} from './hasura-module-definition'; import { HASURA_EVENT_HANDLER, - HASURA_MODULE_CONFIG, HASURA_SCHEDULED_EVENT_HANDLER, } from './hasura.constants'; import { InjectHasuraConfig } from './hasura.decorators'; @@ -44,34 +46,30 @@ function isHasuraScheduledEventPayload( @Module({ imports: [DiscoveryModule], + providers: [ + { + provide: Symbol('CONTROLLER_HACK'), + inject: [MODULE_OPTIONS_TOKEN], + useFactory: (config: HasuraModuleConfig) => { + const controllerPrefix = config.controllerPrefix || 'hasura'; + + Reflect.defineMetadata( + PATH_METADATA, + controllerPrefix, + EventHandlerController, + ); + config.decorators?.forEach((deco) => { + deco(EventHandlerController); + }); + }, + }, + EventHandlerService, + HasuraEventHandlerHeaderGuard, + ], controllers: [EventHandlerController], }) export class HasuraModule - extends createConfigurableDynamicRootModule( - HASURA_MODULE_CONFIG, - { - providers: [ - { - provide: Symbol('CONTROLLER_HACK'), - useFactory: (config: HasuraModuleConfig) => { - const controllerPrefix = config.controllerPrefix || 'hasura'; - - Reflect.defineMetadata( - PATH_METADATA, - controllerPrefix, - EventHandlerController, - ); - config.decorators?.forEach((deco) => { - deco(EventHandlerController); - }); - }, - inject: [HASURA_MODULE_CONFIG], - }, - EventHandlerService, - HasuraEventHandlerHeaderGuard, - ], - }, - ) + extends ConfigurableModuleClass implements OnModuleInit { private readonly logger = new Logger(HasuraModule.name); diff --git a/packages/hasura/src/tests/hasura.event-handling.spec.ts b/packages/hasura/src/tests/hasura.event-handling.spec.ts index 9c40a54b1..5ed6795be 100644 --- a/packages/hasura/src/tests/hasura.event-handling.spec.ts +++ b/packages/hasura/src/tests/hasura.event-handling.spec.ts @@ -106,10 +106,10 @@ describe.each(cases)( beforeEach(async () => { const moduleImport = moduleType === 'forRootAsync' - ? HasuraModule.forRootAsync(HasuraModule, { + ? HasuraModule.forRootAsync({ useFactory: () => moduleConfig, }) - : HasuraModule.forRoot(HasuraModule, moduleConfig); + : HasuraModule.forRoot(moduleConfig); const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [moduleImport], @@ -169,8 +169,8 @@ describe.each(cases)( expect(scheduledEventHandler).toHaveBeenCalledTimes(1); expect(scheduledEventHandler).toHaveBeenCalledWith( expect.objectContaining( - pick(scheduledOneOffEventPayload, ['comment', 'payload']) - ) + pick(scheduledOneOffEventPayload, ['comment', 'payload']), + ), ); }); @@ -184,18 +184,18 @@ describe.each(cases)( expect(scheduledEventHandler).toHaveBeenCalledTimes(1); expect(scheduledEventHandler).toHaveBeenCalledWith( expect.objectContaining( - pick(scheduledEventPayload, ['name', 'payload']) - ) + pick(scheduledEventPayload, ['name', 'payload']), + ), ); }); - } + }, ); describe('HasuraModule with Custom Decorator', () => { it('should call the decorator and set metadata for the controller', async () => { await Test.createTestingModule({ imports: [ - HasuraModule.forRoot(HasuraModule, { + HasuraModule.forRoot({ decorators: [SetMetadata()], webhookConfig: { secretHeader, @@ -206,7 +206,7 @@ describe('HasuraModule with Custom Decorator', () => { }).compile(); const controllerMeta = Reflect.getMetadata( 'TEST:METADATA', - EventHandlerController + EventHandlerController, ); expect(controllerMeta).toBe('metadata'); }); diff --git a/packages/hasura/src/tests/hasura.metadata.spec-utils.ts b/packages/hasura/src/tests/hasura.metadata.spec-utils.ts index b7899d8cf..9e826bd0f 100644 --- a/packages/hasura/src/tests/hasura.metadata.spec-utils.ts +++ b/packages/hasura/src/tests/hasura.metadata.spec-utils.ts @@ -35,12 +35,12 @@ export const yamlFileToJson = (filePath: string) => { }; export const getVersionedMetadataPathAndConfig = ( - v: string + v: string, ): [string, HasuraModuleConfig] => { const version = metadataVersion.parse(v); const metadataPath = path.join( __dirname, - `../../test/__fixtures__/hasura/${version}/metadata` + `../../test/__fixtures__/hasura/${version}/metadata`, ); const { managedMetaDataConfig } = baseConfig; diff --git a/packages/hasura/src/tests/hasura.metadata.spec.ts b/packages/hasura/src/tests/hasura.metadata.spec.ts index b3f5fa7ec..fc0bc2d99 100644 --- a/packages/hasura/src/tests/hasura.metadata.spec.ts +++ b/packages/hasura/src/tests/hasura.metadata.spec.ts @@ -44,7 +44,7 @@ describe('Hasura Metadata', () => { beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [HasuraModule.forRoot(HasuraModule, moduleConfig)], + imports: [HasuraModule.forRoot(moduleConfig)], providers: [TestEventHandlerService], }).compile(); @@ -70,7 +70,7 @@ describe('Hasura Metadata', () => { beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [HasuraModule.forRoot(HasuraModule, moduleConfig)], + imports: [HasuraModule.forRoot(moduleConfig)], providers: [TestEventHandlerService], }).compile(); @@ -97,7 +97,7 @@ describe('Hasura Metadata', () => { beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [HasuraModule.forRoot(HasuraModule, moduleConfig)], + imports: [HasuraModule.forRoot(moduleConfig)], providers: [TestEventHandlerService], }).compile(); diff --git a/packages/modules/src/dynamicModules.ts b/packages/modules/src/dynamicModules.ts index ae623246f..b9288f2c6 100644 --- a/packages/modules/src/dynamicModules.ts +++ b/packages/modules/src/dynamicModules.ts @@ -27,7 +27,7 @@ export type AsyncModuleConfig = Pick & export function createModuleConfigProvider( provide: InjectionToken, - options: AsyncModuleConfig + options: AsyncModuleConfig, ): Provider[] { if ('useFactory' in options) { return [ @@ -40,7 +40,7 @@ export function createModuleConfigProvider( } const optionsProviderGenerator = ( - inject: InjectionToken | OptionalFactoryDependency + inject: InjectionToken | OptionalFactoryDependency, ): Provider => ({ provide, useFactory: async (moduleConfigFactory: ModuleConfigFactory) => { @@ -62,7 +62,7 @@ export function createModuleConfigProvider( return [ optionsProviderGenerator( options.useExisting.provide ?? - options.useExisting.value.constructor.name + options.useExisting.value.constructor.name, ), { provide: @@ -85,15 +85,22 @@ export interface IConfigurableDynamicRootModule { forRootAsync( moduleCtor: Type, - asyncModuleConfig: AsyncModuleConfig + asyncModuleConfig: AsyncModuleConfig, ): DynamicModule; externallyConfigured( moduleCtor: Type, - wait: number + wait: number, ): Promise; } +/** + * + * @deprecated Please use Nestjs Configurable Module {@see https://docs.nestjs.com/fundamentals/dynamic-modules#configurable-module-builder} + * @param moduleConfigToken + * @param moduleProperties + * @returns + */ export function createConfigurableDynamicRootModule( moduleConfigToken: InjectionToken, moduleProperties: Partial< @@ -102,14 +109,14 @@ export function createConfigurableDynamicRootModule( imports: [], exports: [], providers: [], - } + }, ) { abstract class DynamicRootModule { static moduleSubject = new Subject(); static forRootAsync( moduleCtor: Type, - asyncModuleConfig: AsyncModuleConfig + asyncModuleConfig: AsyncModuleConfig, ): DynamicModule { const dynamicModule = { module: moduleCtor, @@ -154,19 +161,19 @@ export function createConfigurableDynamicRootModule( static async externallyConfigured( moduleCtor: Type, - wait: number + wait: number, ): Promise { const timeout$ = interval(wait).pipe( first(), map(() => { throw new Error( - `Expected ${moduleCtor.name} to be configured by at last one Module but it was not configured within ${wait}ms` + `Expected ${moduleCtor.name} to be configured by at last one Module but it was not configured within ${wait}ms`, ); - }) + }), ); return lastValueFrom( - race(timeout$, DynamicRootModule.moduleSubject.pipe(first())) + race(timeout$, DynamicRootModule.moduleSubject.pipe(first())), ); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85a245a3b..7a6895fab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -212,15 +212,9 @@ importers: packages/hasura: dependencies: - '@golevelup/nestjs-common': - specifier: workspace:^ - version: link:../common '@golevelup/nestjs-discovery': specifier: workspace:^ version: link:../discovery - '@golevelup/nestjs-modules': - specifier: workspace:^ - version: link:../modules '@hasura/metadata': specifier: ^1.0.2 version: 1.0.2