Skip to content

Commit

Permalink
Add service/method name to RpcError (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcready authored Jan 4, 2022
1 parent 5c6f50f commit bc3aaf9
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 18 deletions.
10 changes: 10 additions & 0 deletions packages/grpc-transport/src/grpc-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export class GrpcTransport implements RpcTransport {
}
if (err) {
const e = new RpcError(err.message, GrpcStatus[err.code], metadataFromGrpc(err.metadata));
e.methodName = method.name;
e.serviceName = method.service.typeName;
defHeader.rejectPending(e);
defMessage.rejectPending(e);
defStatus.rejectPending(e);
Expand All @@ -105,6 +107,8 @@ export class GrpcTransport implements RpcTransport {
// we require that the status is an error status.
if (defMessage.state === DeferredState.PENDING && val.code === GrpcStatus.OK) {
const e = new RpcError('expected error status', GrpcStatus[GrpcStatus.DATA_LOSS]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
defMessage.rejectPending(e);
defStatus.rejectPending(e);
defTrailer.rejectPending(e);
Expand Down Expand Up @@ -152,6 +156,8 @@ export class GrpcTransport implements RpcTransport {

gCall.addListener('error', err => {
const e = isServiceError(err) ? new RpcError(err.message, GrpcStatus[err.code], metadataFromGrpc(err.metadata)) : new RpcError(err.message);
e.methodName = method.name;
e.serviceName = method.service.typeName;
defHeader.rejectPending(e);
if (!outStream.closed) {
outStream.notifyError(e);
Expand Down Expand Up @@ -210,6 +216,8 @@ export class GrpcTransport implements RpcTransport {
}
if (err) {
const e = new RpcError(err.message, GrpcStatus[err.code], metadataFromGrpc(err.metadata));
e.methodName = method.name;
e.serviceName = method.service.typeName;
defHeader.rejectPending(e);
defMessage.rejectPending(e);
defStatus.rejectPending(e);
Expand Down Expand Up @@ -273,6 +281,8 @@ export class GrpcTransport implements RpcTransport {

gCall.addListener('error', err => {
const e = isServiceError(err) ? new RpcError(err.message, GrpcStatus[err.code], metadataFromGrpc(err.metadata)) : new RpcError(err.message);
e.methodName = method.name;
e.serviceName = method.service.typeName;
defHeader.rejectPending(e);
if (!outStream.closed) {
outStream.notifyError(e);
Expand Down
34 changes: 32 additions & 2 deletions packages/grpcweb-transport/spec/grpc-web-transport.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,23 @@ describe('GrpcWebFetchTransport', () => {
describe('clientStreaming()', function () {
it('throws because it is not supported in grpc-web', async () => {
const transport = new GrpcWebFetchTransport({ baseUrl: '' });
const methodInfo: MethodInfo<RequestMessage, ResponseMessage> = {
service: {
typeName: "test.Service",
methods: [],
options: {},
},
name: "clientStreaming",
I: RequestMessage,
O: ResponseMessage,
localName: "clientStreaming",
idempotency: undefined,
clientStreaming: true,
serverStreaming: false,
options: {},
};
try {
await transport.clientStreaming();
await transport.clientStreaming(methodInfo);
fail('this should not be implemented');
} catch (e) {
expect(e).toBeInstanceOf(RpcError);
Expand All @@ -93,8 +108,23 @@ describe('GrpcWebFetchTransport', () => {
describe('duplex()', function () {
it('throws because it is not supported in grpc-web', async () => {
const transport = new GrpcWebFetchTransport({ baseUrl: '' });
const methodInfo: MethodInfo<RequestMessage, ResponseMessage> = {
service: {
typeName: "test.Service",
methods: [],
options: {},
},
name: "duplex",
I: RequestMessage,
O: ResponseMessage,
localName: "duplex",
idempotency: undefined,
clientStreaming: true,
serverStreaming: true,
options: {},
};
try {
await transport.duplex();
await transport.duplex(methodInfo);
fail('this should not be implemented');
} catch (e) {
expect(e).toBeInstanceOf(RpcError);
Expand Down
18 changes: 14 additions & 4 deletions packages/grpcweb-transport/src/grpc-web-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,18 @@ export class GrpcWebFetchTransport implements RpcTransport {
}


clientStreaming<I extends object, O extends object>(/*method: MethodInfo<I, O>, options: RpcOptions*/): ClientStreamingCall<I, O> {
throw new RpcError('Client streaming is not supported by grpc-web', GrpcStatusCode[GrpcStatusCode.UNIMPLEMENTED]);
clientStreaming<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): ClientStreamingCall<I, O> {
const e = new RpcError('Client streaming is not supported by grpc-web', GrpcStatusCode[GrpcStatusCode.UNIMPLEMENTED]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

duplex<I extends object, O extends object>(/*method: MethodInfo<I, O>, options: RpcOptions*/): DuplexStreamingCall<I, O> {
throw new RpcError('Duplex streaming is not supported by grpc-web', GrpcStatusCode[GrpcStatusCode.UNIMPLEMENTED]);
duplex<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): DuplexStreamingCall<I, O> {
const e = new RpcError('Duplex streaming is not supported by grpc-web', GrpcStatusCode[GrpcStatusCode.UNIMPLEMENTED]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}


Expand Down Expand Up @@ -158,6 +164,8 @@ export class GrpcWebFetchTransport implements RpcTransport {
else
// RpcErrors are thrown by us, everything else is an internal error
error = new RpcError(reason instanceof Error ? reason.message : "" + reason, GrpcStatusCode[GrpcStatusCode.INTERNAL]);
error.methodName = method.name;
error.serviceName = method.service.typeName;
defHeader.rejectPending(error);
responseStream.notifyError(error)
defStatus.rejectPending(error);
Expand Down Expand Up @@ -264,6 +272,8 @@ export class GrpcWebFetchTransport implements RpcTransport {
else
// RpcErrors are thrown by us, everything else is an internal error
error = new RpcError(reason instanceof Error ? reason.message : "" + reason, GrpcStatusCode[GrpcStatusCode.INTERNAL]);
error.methodName = method.name;
error.serviceName = method.service.typeName;
defHeader.rejectPending(error);
defMessage.rejectPending(error);
defStatus.rejectPending(error);
Expand Down
23 changes: 17 additions & 6 deletions packages/runtime-angular/src/lib/twirp-transport.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ export class TwirpTransport implements RpcTransport {
// RpcErrors are thrown by us, everything else is an internal error
let error = reason instanceof RpcError ? reason
: new RpcError(reason instanceof Error ? reason.message : reason, TwirpErrorCode[TwirpErrorCode.internal]);
error.methodName = method.name;
error.serviceName = method.service.typeName;
defHeader.rejectPending(error);
defMessage.rejectPending(error);
defStatus.rejectPending(error);
Expand Down Expand Up @@ -168,16 +170,25 @@ export class TwirpTransport implements RpcTransport {
}


clientStreaming<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, options: RpcOptions*/): ClientStreamingCall<I, O> {
throw new RpcError('Client streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
clientStreaming<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): ClientStreamingCall<I, O> {
const e = new RpcError('Client streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

duplex<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, options: RpcOptions*/): DuplexStreamingCall<I, O> {
throw new RpcError('Duplex streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
duplex<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): DuplexStreamingCall<I, O> {
const e = new RpcError('Duplex streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

serverStreaming<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, input: I, options?: RpcOptions*/): ServerStreamingCall<I, O> {
throw new RpcError('Server streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
serverStreaming<I extends object, O extends object>(method: MethodInfo<I, O>/*, input: I, options?: RpcOptions*/): ServerStreamingCall<I, O> {
const e = new RpcError('Server streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

}
Expand Down
3 changes: 3 additions & 0 deletions packages/runtime-rpc/spec/rpc-error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ describe('RpcError', () => {
foo: "bar"
});
expect(err.toString()).toBe('RpcError: msg\n\nCode: cde\n\nMeta:\n x: y\n foo: bar');
err.methodName = "Qux";
err.serviceName = "Foo.Bar.Baz";
expect(err.toString()).toBe('RpcError: msg\n\nCode: cde\nMethod: Foo.Bar.Baz/Qux\n\nMeta:\n x: y\n foo: bar');
});


Expand Down
21 changes: 21 additions & 0 deletions packages/runtime-rpc/src/rpc-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ export class RpcError extends Error {
*/
meta: RpcMetadata;

/**
* The name of the RPC method that was called as declared in .proto
*/
methodName?: string;

/**
* The name of the RPC service that was called as declared in .proto
*
* It will be in the form of:
* - package name
* - dot "."
* - service name
*
* If the service was declared without a package, the package name and dot
* are omitted.
*/
serviceName?: string;

name = 'RpcError';

constructor(message: string, code = 'UNKNOWN', meta?: RpcMetadata) {
Expand All @@ -39,6 +57,9 @@ export class RpcError extends Error {
l.push('');
l.push('Code: ' + this.code);
}
if (this.serviceName && this.methodName) {
l.push('Method: ' + this.serviceName + '/' + this.methodName)
}
let m = Object.entries(this.meta);
if (m.length) {
l.push('');
Expand Down
23 changes: 17 additions & 6 deletions packages/twirp-transport/src/twirp-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export class TwirpFetchTransport implements RpcTransport {
// RpcErrors are thrown by us, everything else is an internal error
let error = reason instanceof RpcError ? reason
: new RpcError(reason instanceof Error ? reason.message : reason, TwirpErrorCode[TwirpErrorCode.internal]);
error.methodName = method.name;
error.serviceName = method.service.typeName;
defHeader.rejectPending(error);
defMessage.rejectPending(error);
defStatus.rejectPending(error);
Expand Down Expand Up @@ -161,16 +163,25 @@ export class TwirpFetchTransport implements RpcTransport {
}


clientStreaming<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, options: RpcOptions*/): ClientStreamingCall<I, O> {
throw new RpcError('Client streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
clientStreaming<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): ClientStreamingCall<I, O> {
const e = new RpcError('Client streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

duplex<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, options: RpcOptions*/): DuplexStreamingCall<I, O> {
throw new RpcError('Duplex streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
duplex<I extends object, O extends object>(method: MethodInfo<I, O>/*, options: RpcOptions*/): DuplexStreamingCall<I, O> {
const e = new RpcError('Duplex streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

serverStreaming<I extends object, O extends object>(/*service: ServiceInfo, method: MethodInfo<I, O>, input: I, options?: RpcOptions*/): ServerStreamingCall<I, O> {
throw new RpcError('Server streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
serverStreaming<I extends object, O extends object>(method: MethodInfo<I, O>/*, input: I, options?: RpcOptions*/): ServerStreamingCall<I, O> {
const e = new RpcError('Server streaming is not supported by Twirp', TwirpErrorCode[TwirpErrorCode.unimplemented]);
e.methodName = method.name;
e.serviceName = method.service.typeName;
throw e;
}

}

0 comments on commit bc3aaf9

Please sign in to comment.