Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(microservices): add status, unwrap, on, and other features #14142

Merged
merged 11 commits into from
Nov 20, 2024
6 changes: 3 additions & 3 deletions integration/microservices/e2e/sum-rpc-async.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import {
Injectable,
Module,
} from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import {
AsyncOptions,
ClientTCP,
ClientsModule,
MessagePattern,
MicroserviceOptions,
Payload,
TcpClientOptions,
TcpOptions,
Transport,
} from '@nestjs/microservices';
import { expect } from 'chai';
import { NestFactory } from '@nestjs/core';

let port: number;

Expand All @@ -25,7 +25,7 @@ do {

@Injectable()
class RpcOptionsProvider {
getOptions(): TcpClientOptions {
getOptions(): TcpOptions {
return {
transport: Transport.TCP,
options: {
Expand Down
8 changes: 4 additions & 4 deletions integration/microservices/src/tcp-tls/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Module, Injectable } from '@nestjs/common';
import { AppController } from './app.controller';
import { Injectable, Module } from '@nestjs/common';
import {
ClientOptions,
ClientsModule,
Transport,
ClientsModuleOptionsFactory,
ClientOptions,
ClientTCP,
RpcException,
Transport,
} from '@nestjs/microservices';
import { AppController } from './app.controller';

import * as fs from 'fs';
import * as path from 'path';
Expand Down
42 changes: 35 additions & 7 deletions packages/common/interfaces/nest-microservice.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Observable } from 'rxjs';
import { ExceptionFilter } from './exceptions/exception-filter.interface';
import { CanActivate } from './features/can-activate.interface';
import { NestInterceptor } from './features/nest-interceptor.interface';
Expand All @@ -19,46 +20,73 @@ export interface INestMicroservice extends INestApplicationContext {
listen(): Promise<any>;

/**
* Register Ws Adapter which will be used inside Gateways.
* Use when you want to override default `socket.io` library.
* Registers a web socket adapter that will be used for Gateways.
* Use to override the default `socket.io` library.
*
* @param {WebSocketAdapter} adapter
* @returns {this}
*/
useWebSocketAdapter(adapter: WebSocketAdapter): this;

/**
* Registers exception filters as global filters (will be used within every message pattern handler)
* Registers global exception filters (will be used for every pattern handler).
*
* @param {...ExceptionFilter} filters
*/
useGlobalFilters(...filters: ExceptionFilter[]): this;

/**
* Registers pipes as global pipes (will be used within every message pattern handler)
* Registers global pipes (will be used for every pattern handler).
*
* @param {...PipeTransform} pipes
*/
useGlobalPipes(...pipes: PipeTransform<any>[]): this;

/**
* Registers interceptors as global interceptors (will be used within every message pattern handler)
* Registers global interceptors (will be used for every pattern handler).
*
* @param {...NestInterceptor} interceptors
*/
useGlobalInterceptors(...interceptors: NestInterceptor[]): this;

/**
* Registers guards as global guards (will be used within every message pattern handler)
* Registers global guards (will be used for every pattern handler).
*
* @param {...CanActivate} guards
*/
useGlobalGuards(...guards: CanActivate[]): this;

/**
* Terminates the application
* Terminates the application.
*
* @returns {Promise<void>}
*/
close(): Promise<void>;

/**
* Returns an observable that emits status changes.
*
* @returns {Observable<string>}
*/
status: Observable<string>;

/**
* Registers an event listener for the given event.
* @param event Event name
* @param callback Callback to be executed when the event is emitted
*/
on<
EventsMap extends Record<string, Function> = Record<string, Function>,
EventKey extends keyof EventsMap = keyof EventsMap,
EventCallback extends EventsMap[EventKey] = EventsMap[EventKey],
>(
event: EventKey,
callback: EventCallback,
): void;

/**
* Returns an instance of the underlying server/broker instance,
* or a group of servers if there are more than one.
*/
unwrap<T>(): T;
}
6 changes: 5 additions & 1 deletion packages/core/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,11 @@ export class DependenciesScanner {
// Skip "InternalCoreModule" from calculating distance
modulesGenerator.next();

const calculateDistance = (moduleRef: Module, distance = 1, modulesStack = []) => {
const calculateDistance = (
moduleRef: Module,
distance = 1,
modulesStack = [],
) => {
const localModulesStack = [...modulesStack];
if (!moduleRef || localModulesStack.includes(moduleRef)) {
return;
Expand Down
44 changes: 37 additions & 7 deletions packages/microservices/client/client-grpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,42 @@ import { GRPC_DEFAULT_PROTO_LOADER, GRPC_DEFAULT_URL } from '../constants';
import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception';
import { ClientGrpc, GrpcOptions } from '../interfaces';
import { ClientProxy } from './client-proxy';
import { GRPC_CANCELLED } from './constants';
import { ChannelOptions } from '../external/grpc-options.interface';
import { getGrpcPackageDefinition } from '../helpers';
import { ClientGrpc, GrpcOptions } from '../interfaces';
import { ClientProxy } from './client-proxy';

let grpcPackage: any = {};
let grpcProtoLoaderPackage: any = {};
const GRPC_CANCELLED = 'Cancelled';

// To enable type safety for gRPC. This cant be uncommented by default
// because it would require the user to install the @grpc/grpc-js package even if they dont use gRPC
// Otherwise, TypeScript would fail to compile the code.
//
// type GrpcClient = import('@grpc/grpc-js').Client;
Comment on lines +16 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could add an tsconfig.json#compilerOptions.paths entry that replaces these optional dependencies with any on-compile 🤔 You'd get type-safety in the IDE, or when running tsc --noEmit

This would require a separate tsconfigs for development and compilation, and fiddling with the build system is probably not worth the small gain in DX.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsc does not auto-replace paths from tsconfig.json#compilerOptions.paths (it leaves them as is)

// let grpcPackage = {} as typeof import('@grpc/grpc-js');
// let grpcProtoLoaderPackage = {} as typeof import('@grpc/proto-loader');

type GrpcClient = any;
let grpcPackage = {} as any;
let grpcProtoLoaderPackage = {} as any;

/**
* @publicApi
*/
export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
export class ClientGrpcProxy
extends ClientProxy<never, never>
implements ClientGrpc
{
protected readonly logger = new Logger(ClientProxy.name);
protected readonly clients = new Map<string, any>();
protected readonly url: string;
protected grpcClients = [];
protected grpcClients: GrpcClient[] = [];

get status(): never {
throw new Error(
'The "status" attribute is not supported by the gRPC transport',
);
}

constructor(protected readonly options: GrpcOptions['options']) {
super();
Expand Down Expand Up @@ -367,4 +386,15 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
'Method is not supported in gRPC mode. Use ClientGrpc instead (learn more in the documentation).',
);
}

public on<EventKey extends never = never, EventCallback = any>(
event: EventKey,
callback: EventCallback,
) {
throw new Error('Method is not supported in gRPC mode.');
}

public unwrap<T>(): T {
throw new Error('Method is not supported in gRPC mode.');
}
}
Loading