feat: allow for microservice options to come from the di container

Microservices are now able to be created by getting their options
from within the DI container itself. This has been a long requested
feature of developers and I finally had some time to work through how
we could possibly let this happen.
This commit is contained in:
Jay McDoniel
2023-10-20 14:21:33 -07:00
parent ecdd86f688
commit 2f2252264b
3 changed files with 130 additions and 8 deletions

View File

@@ -0,0 +1,98 @@
import {
Controller,
INestMicroservice,
Injectable,
Module,
} from '@nestjs/common';
import {
AsyncOptions,
ClientTCP,
ClientsModule,
MessagePattern,
MicroserviceOptions,
Payload,
TcpClientOptions,
Transport,
} from '@nestjs/microservices';
import { expect } from 'chai';
import { NestFactory } from '@nestjs/core';
let port: number;
do {
port = Math.round(Math.random() * 10000);
} while (port < 1000);
@Injectable()
class RpcOptionsProvider {
getOptions(): TcpClientOptions {
return {
transport: Transport.TCP,
options: {
port,
host: '0.0.0.0',
},
};
}
}
@Controller()
class RpcController {
@MessagePattern({ cmd: 'sum' })
sumPayload(@Payload() payload: number[]) {
return payload.reduce((a, b) => a + b, 0);
}
}
@Module({
imports: [
ClientsModule.register([
{
name: 'RPC_CLIENT',
transport: Transport.TCP,
options: {
port,
host: '0.0.0.0',
},
},
]),
],
controllers: [RpcController],
providers: [RpcOptionsProvider],
})
class RpcModule {}
describe('RPC Async transport', () => {
let app: INestMicroservice;
let client: ClientTCP;
beforeEach(async () => {
app = await NestFactory.createMicroservice<
AsyncOptions<MicroserviceOptions>
>(RpcModule, {
logger: false,
inject: [RpcOptionsProvider],
useFactory: (optionsProvider: RpcOptionsProvider) =>
optionsProvider.getOptions(),
});
await app.listen();
client = app.get('RPC_CLIENT', { strict: false });
});
it(`/POST`, done => {
let retData = 0;
client.send({ cmd: 'sum' }, [1, 2, 3, 4, 5]).subscribe({
next: val => (retData += val),
error: done,
complete: () => {
expect(retData).to.eq(15);
done();
},
});
});
afterEach(async () => {
await app.close();
});
});

View File

@@ -1,4 +1,4 @@
import { Type } from '@nestjs/common';
import { FactoryProvider, InjectionToken, Type } from '@nestjs/common';
import { ConnectionOptions } from 'tls';
import { Transport } from '../enums/transport.enum';
import { ChannelOptions } from '../external/grpc-options.interface';
@@ -28,6 +28,16 @@ export type MicroserviceOptions =
| KafkaOptions
| CustomStrategy;
export type AsyncMicroserviceOptions = {
inject: InjectionToken[];
useFactory: (...args: any[]) => MicroserviceOptions;
};
export type AsyncOptions<T extends object> = {
inject: InjectionToken[];
useFactory: (...args: any[]) => T;
};
/**
* @publicApi
*/

View File

@@ -17,7 +17,10 @@ import { GraphInspector } from '@nestjs/core/inspector/graph-inspector';
import { NestApplicationContext } from '@nestjs/core/nest-application-context';
import { Transport } from './enums/transport.enum';
import { CustomTransportStrategy } from './interfaces/custom-transport-strategy.interface';
import { MicroserviceOptions } from './interfaces/microservice-configuration.interface';
import {
AsyncMicroserviceOptions,
MicroserviceOptions,
} from './interfaces/microservice-configuration.interface';
import { MicroservicesModule } from './microservices-module';
import { Server } from './server/server';
import { ServerFactory } from './server/server-factory';
@@ -43,7 +46,8 @@ export class NestMicroservice
constructor(
container: NestContainer,
config: NestMicroserviceOptions & MicroserviceOptions = {},
config: NestMicroserviceOptions &
(MicroserviceOptions | AsyncMicroserviceOptions) = {},
private readonly graphInspector: GraphInspector,
private readonly applicationConfig: ApplicationConfig,
) {
@@ -60,12 +64,22 @@ export class NestMicroservice
this.selectContextModule();
}
public createServer(config: NestMicroserviceOptions & MicroserviceOptions) {
public createServer(
config: NestMicroserviceOptions &
(MicroserviceOptions | AsyncMicroserviceOptions),
) {
try {
this.microserviceConfig = {
transport: Transport.TCP,
...config,
} as any;
if ('useFactory' in config) {
const args = config.inject?.map(token =>
this.get(token, { strict: false }),
);
this.microserviceConfig = config.useFactory(...args);
} else {
this.microserviceConfig = {
transport: Transport.TCP,
...config,
} as any;
}
const { strategy } = config as any;
this.server = strategy
? strategy