Skip to content

Commit

Permalink
fix: do not fail when decoding unknown error (#1077)
Browse files Browse the repository at this point in the history
Fixes #1076. `google-gax` has no way to know of all possible error protos, so we'll just skip and ignore those that cannot be decoded.
  • Loading branch information
alexander-fenster authored Aug 5, 2021
1 parent 6ef89f1 commit 90e19b1
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 24 deletions.
23 changes: 18 additions & 5 deletions src/googleError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,19 @@ export class GoogleErrorDecoder {

// google.rpc.Status contains an array of google.protobuf.Any
// which need a special treatment
const details: Array<protobuf.Message> = [];
for (const detail of status.details) {
try {
const decodedDetail = this.decodeProtobufAny(detail);
details.push(decodedDetail);
} catch (err) {
// cannot decode detail, likely because of the unknown type - just skip it
}
}
const result = {
code: status.code,
message: status.message,
details: status.details.map(detail => this.decodeProtobufAny(detail)),
details,
};
return result;
}
Expand All @@ -98,6 +107,7 @@ export class GoogleErrorDecoder {
decodeErrorFromBuffer(buffer: Buffer | ArrayBuffer): Error {
return this.callErrorFromStatus(this.decodeRpcStatus(buffer));
}

// Decodes gRPC-fallback error which is an instance of google.rpc.Status.
decodeRpcStatusDetails(
bufferArr: Buffer[] | ArrayBuffer[]
Expand All @@ -108,10 +118,13 @@ export class GoogleErrorDecoder {
const error_status = this.statusType.decode(
uint8array
) as unknown as RpcStatus;
const status_details_array = error_status.details.map(detail =>
this.decodeProtobufAny(detail)
);
status.push(status_details_array[0]);
for (const detail of error_status.details) {
try {
status.push(this.decodeProtobufAny(detail));
} catch (err) {
// cannot decode detail, likely because of the unknown type - just skip it
}
}
});
return status;
}
Expand Down
65 changes: 46 additions & 19 deletions test/unit/googleError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@ interface MyObj {
}

describe('gRPC-google error decoding', () => {
const fixtureName = path.resolve(
__dirname,
'..',
'fixtures',
'multipleErrors.json'
);
const protos_path = path.resolve(
__dirname,
'..',
'..',
'protos',
'google',
'rpc'
);
const root = protobuf.loadSync([
path.join(protos_path, 'error_details.proto'),
path.join(protos_path, 'status.proto'),
]);

it('decodes multiple errors', async () => {
// example of when there are multiple errors available to be decoded
const bufferArr = [] as Buffer[];
Expand All @@ -36,26 +55,7 @@ describe('gRPC-google error decoding', () => {
const decoder = new GoogleErrorDecoder();
const readFile = util.promisify(fs.readFile);

const fixtureName = path.resolve(
__dirname,
'..',
'fixtures',
'multipleErrors.json'
);
const protos_path = path.resolve(
__dirname,
'..',
'..',
'protos',
'google',
'rpc'
);

const data = await readFile(fixtureName, 'utf8');
const root = protobuf.loadSync([
path.join(protos_path, 'error_details.proto'),
path.join(protos_path, 'status.proto'),
]);
const objs = JSON.parse(data) as MyObj[];
for (const obj of objs) {
const MessageType = root.lookupType(obj.type);
Expand Down Expand Up @@ -88,6 +88,33 @@ describe('gRPC-google error decoding', () => {
assert.strictEqual(JSON.stringify(decodedError), JSON.stringify([]));
});

it('DecodeRpcStatus does not fail when unknown type is encoded', () => {
const any = {type_url: 'noMatch', value: new Uint8Array()};
const status = {code: 3, message: 'test', details: [any]};
const Status = root.lookupType('google.rpc.Status');
const status_buffer = Status.encode(status).finish();
const decoder = new GoogleErrorDecoder();

const decodedError = decoder.decodeRpcStatus(status_buffer);

assert.strictEqual(
JSON.stringify(decodedError),
'{"code":3,"message":"test","details":[]}'
);
});

it('DecodeRpcStatusDetails does not fail when unknown type is encoded', () => {
const any = {type_url: 'noMatch', value: new Uint8Array()};
const status = {code: 3, message: 'test', details: [any]};
const Status = root.lookupType('google.rpc.Status');
const status_buffer = Status.encode(status).finish();
const decoder = new GoogleErrorDecoder();

const decodedError = decoder.decodeRpcStatusDetails([status_buffer]);

assert.strictEqual(JSON.stringify(decodedError), JSON.stringify([]));
});

it('does not decode when unknown type is encoded in type_url', () => {
// example of when error details' type_url doesn't match "type.googleapis.com"
const decoder = new GoogleErrorDecoder();
Expand Down

0 comments on commit 90e19b1

Please sign in to comment.