Skip to content

Commit

Permalink
feature(grpc): add grpc exception filter
Browse files Browse the repository at this point in the history
  • Loading branch information
tychota committed Apr 10, 2019
1 parent 2ec60ce commit 5dd17d4
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 9 deletions.
10 changes: 10 additions & 0 deletions integration/microservices/e2e/sum-grpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ describe('GRPC transport', () => {
.expect(200, { result: 15 });
});

it(`handle exception correctly`, () => {
return (
request(server)
.post('/divide')
.send({ first: 42, last: 0 })
// TODO: this will change once the grpc client raise the closest HTTP exception to OutOfBound
.expect(500, { message: 'Dividing by 0 is Strictly Forbidden' })
);
});

afterEach(async () => {
await app.close();
});
Expand Down
35 changes: 32 additions & 3 deletions integration/microservices/src/grpc/grpc.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
import { Client, ClientGrpc, GrpcMethod, Transport } from '@nestjs/microservices';
import { Body, Controller, HttpCode, Post, UseFilters } from '@nestjs/common';
import {
Client,
ClientGrpc,
GrpcMethod,
Transport,
GrpcOutOfRangeException,
GrpcExceptionFilter,
} from '@nestjs/microservices';
import { join } from 'path';
import { Observable, of } from 'rxjs';

interface DivideParams {
first: number;
last: number;
}

@Controller()
export class GrpcController {
@Client({
Expand All @@ -16,7 +28,7 @@ export class GrpcController {

@Post()
@HttpCode(200)
call(@Body() data: number[]): Observable<number> {
sumHttp(@Body() data: number[]): Observable<number> {
const svc = this.client.getService<any>('Math');
return svc.sum({ data });
}
Expand All @@ -27,4 +39,21 @@ export class GrpcController {
result: data.reduce((a, b) => a + b),
});
}

@Post('/divide')
@HttpCode(200)
divideHttp(@Body() { first, last }: DivideParams): Observable<number> {
const svc = this.client.getService<any>('Math');
return svc.sum({ first, last });
}

@UseFilters(new GrpcExceptionFilter())
@GrpcMethod('Math')
async divide({ first, last }: DivideParams): Promise<any> {
const result = first / last;
if (!isFinite(result)) {
throw new GrpcOutOfRangeException('Dividing by 0 is Strictly Forbidden');
}
return result;
}
}
16 changes: 10 additions & 6 deletions integration/microservices/src/grpc/math.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ syntax = "proto3";
package math;

service Math {
rpc Sum (RequestSum) returns (SumResult) {}
rpc Sum(RequestSum) returns (SumResult) {}
rpc Divide(RequestDivide) returns (DivideResult) {}
}

message SumResult {
int32 result = 1;
}
message SumResult { int32 result = 1; }

message RequestSum { repeated int32 data = 1; }

message DivideResult { int32 result = 1; }

message RequestSum {
repeated int32 data = 1;
message RequestDivide {
int32 first = 1;
int32 last = 2;
}
37 changes: 37 additions & 0 deletions packages/microservices/exceptions/grpc-exception-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
Logger,
RpcExceptionFilter,
ArgumentsHost,
Catch,
} from '@nestjs/common';
import { isError, isObject } from '@nestjs/common/utils/shared.utils';
import { MESSAGES } from '@nestjs/core/constants';

import { Observable, throwError } from 'rxjs';

import { GrpcException } from './grpc-exceptions';
import { GrpcStatus } from 'enums/grpc-status.enum';

@Catch()
export class GrpcExceptionFilter<T = any, R = any>
implements RpcExceptionFilter<T> {
private static readonly logger = new Logger('GrpcExceptionsHandler');

catch(exception: T, host: ArgumentsHost): Observable<R> {
if (!(exception instanceof GrpcException)) {
const errorMessage = MESSAGES.UNKNOWN_EXCEPTION_MESSAGE;
const loggerArgs = isError(exception)
? [exception.message, exception.stack]
: [exception];

const logger = GrpcExceptionFilter.logger;
logger.error.apply(logger, loggerArgs as any);

return throwError({ code: GrpcStatus.UNKNOWN, message: errorMessage });
}
const code = exception.getCode();
const error = exception.getError();
const message = isObject(error) ? error : { code, message: error };
return throwError(message);
}
}
1 change: 1 addition & 0 deletions packages/microservices/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './base-rpc-exception-filter';
export * from './rpc-exception';
export * from './grpc-exceptions';
export * from './grpc-exception-filter';

0 comments on commit 5dd17d4

Please sign in to comment.