diff --git a/lib/client/client-google-pubsub.ts b/lib/client/client-google-pubsub.ts index 4e090d3..8781210 100644 --- a/lib/client/client-google-pubsub.ts +++ b/lib/client/client-google-pubsub.ts @@ -30,6 +30,15 @@ export class ClientGooglePubSub extends ClientProxy { this.googlePubSubClient = options?.pubSubClient ?? new PubSub(options?.pubSubClientConfig); } + /** + * Create and return a new ClientGooglePubSub instance + * @param options + * @returns + */ + public static create(options?: GooglePubSubOptions): ClientGooglePubSub { + return new this(options); + } + /** * Since the client on starts listening for messages when event subscribers are added, this * method does nothing diff --git a/lib/client/google-pubsub.module.ts b/lib/client/google-pubsub.module.ts new file mode 100644 index 0000000..8e78817 --- /dev/null +++ b/lib/client/google-pubsub.module.ts @@ -0,0 +1,88 @@ +import { DynamicModule, Module, Provider } from '@nestjs/common'; +import { GOOGLE_PUBSUB_CLIENT_TOKEN, GOOGLE_PUBSUB_MODULE_OPTIONS } from '../constants'; +import { + GooglePubSubModuleAsyncOptions, + GooglePubSubModuleOptionsFactory, + GooglePubSubOptions, +} from '../interfaces'; +import { ClientGooglePubSub } from './client-google-pubsub'; + +// This bit is adapted from the built from http-service: +// https://github.com/nestjs/nest/tree/6a61a593b9d388144ea5497909c03612c298214c/packages/common/http +@Module({ + providers: [ + { + provide: GOOGLE_PUBSUB_CLIENT_TOKEN, + useValue: ClientGooglePubSub, + }, + ], + exports: [GOOGLE_PUBSUB_CLIENT_TOKEN], +}) +export class GooglePubSubModule { + static register(options: GooglePubSubOptions): DynamicModule { + return { + module: GooglePubSubModule, + providers: [ + { + provide: GOOGLE_PUBSUB_CLIENT_TOKEN, + useValue: ClientGooglePubSub.create(options), + }, + ], + }; + } + + static registerAsync(options: GooglePubSubModuleAsyncOptions): DynamicModule { + return { + module: GooglePubSubModule, + imports: options.imports, + providers: [ + ...this.createAsyncProviders(options), + { + provide: GOOGLE_PUBSUB_CLIENT_TOKEN, + useFactory: (config: GooglePubSubOptions) => ClientGooglePubSub.create(config), + inject: [GOOGLE_PUBSUB_MODULE_OPTIONS], + }, + ...(options.extraProviders || []), + ], + }; + } + + private static createAsyncProviders(options: GooglePubSubModuleAsyncOptions): Provider[] { + if (options.useExisting || options.useFactory) { + return [this.createAsyncOptionsProvider(options)]; + } + return [ + this.createAsyncOptionsProvider(options), + { + // TODO: Change the options to a conditional type so we can be assured _at least_ + // one of these is present + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + provide: options.useClass, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + useClass: options.useClass, + }, + ]; + } + + private static createAsyncOptionsProvider(options: GooglePubSubModuleAsyncOptions): Provider { + if (options.useFactory) { + return { + provide: GOOGLE_PUBSUB_MODULE_OPTIONS, + useFactory: options.useFactory, + inject: options.inject || [], + }; + } + return { + provide: GOOGLE_PUBSUB_MODULE_OPTIONS, + useFactory: async (optionsFactory: GooglePubSubModuleOptionsFactory) => + optionsFactory.createGooglePubSubOptions(), + // TODO: Change the options to a conditional type so we can be assured _at least_ + // one of these is present + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + inject: [options.useExisting || options.useClass], + }; + } +} diff --git a/lib/constants.ts b/lib/constants.ts index ecec242..3b41993 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1 +1,3 @@ export const GOOGLE_PUBSUB_SUBSCRIPTION_MESSAGE_EVENT = 'message'; +export const GOOGLE_PUBSUB_CLIENT_TOKEN = 'GOOGLE_PUBSUB_INSTANCE_TOKEN'; +export const GOOGLE_PUBSUB_MODULE_OPTIONS = 'GOOGLE_PUBSUB_MODULE_OPTIONS'; diff --git a/lib/interfaces.ts b/lib/interfaces.ts index 45053f9..2df6519 100644 --- a/lib/interfaces.ts +++ b/lib/interfaces.ts @@ -1,4 +1,5 @@ import { ClientConfig, Message, PubSub, Subscription, Topic } from '@google-cloud/pubsub'; +import { ModuleMetadata, Provider, Type } from '@nestjs/common'; import { ClientGooglePubSub } from './client'; import { GooglePubSubContext } from './ctx-host'; @@ -66,3 +67,15 @@ export interface GooglePubSubOptions { pubSubClient?: PubSub; pubSubClientConfig?: ClientConfig; } + +export interface GooglePubSubModuleOptionsFactory { + createGooglePubSubOptions(): Promise | GooglePubSubOptions; +} + +export interface GooglePubSubModuleAsyncOptions extends Pick { + useExisting?: Type; + useClass?: Type; + useFactory?: (...args: any[]) => Promise | GooglePubSubOptions; + inject?: any[]; + extraProviders?: Provider[]; +}