Skip to content

Commit c497607

Browse files
feat: decode grpc status objects and map them to protocol types
Status object thrown by grpc commands contains metadata that needs to be serialized in order to map it to custom errors generated through proto files grpc/grpc-web#399
1 parent 2ce3d92 commit c497607

File tree

1 file changed

+42
-2
lines changed

1 file changed

+42
-2
lines changed

arduino-ide-extension/src/node/service-error.ts

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
11
import { Metadata, StatusObject } from '@grpc/grpc-js';
2+
import { Status } from './cli-protocol/google/rpc/status_pb';
3+
import { stringToUint8Array } from '../common/utils';
4+
import { ProgrammerIsRequiredForUploadError } from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
5+
6+
type ProtoError = typeof ProgrammerIsRequiredForUploadError;
7+
const protoErrorsMap = new Map<string, ProtoError>([
8+
[
9+
'type.googleapis.com/cc.arduino.cli.commands.v1.ProgrammerIsRequiredForUploadError',
10+
ProgrammerIsRequiredForUploadError,
11+
],
12+
// handle other cli defined errors here
13+
]);
214

315
export type ServiceError = StatusObject & Error;
416
export namespace ServiceError {
517
export function isCancel(arg: unknown): arg is ServiceError & { code: 1 } {
618
return is(arg) && arg.code === 1; // https://grpc.github.io/grpc/core/md_doc_statuscodes.html
719
}
20+
821
export function is(arg: unknown): arg is ServiceError {
9-
return arg instanceof Error && isStatusObjet(arg);
22+
return arg instanceof Error && isStatusObject(arg);
1023
}
11-
function isStatusObjet(arg: unknown): arg is StatusObject {
24+
25+
export function isInstanceOf(arg: unknown, type: unknown): boolean {
26+
if (!isStatusObject(arg)) {
27+
return false;
28+
}
29+
30+
const bin = arg.metadata.get('grpc-status-details-bin')[0];
31+
32+
const uint8Array =
33+
typeof bin === 'string'
34+
? stringToUint8Array(bin)
35+
: new Uint8Array(bin.buffer, bin.byteOffset, bin.byteLength);
36+
37+
const errors = Status.deserializeBinary(uint8Array)
38+
.getDetailsList()
39+
.map((details) => {
40+
const typeUrl = details.getTypeUrl();
41+
const ErrorType = protoErrorsMap.get(typeUrl);
42+
return ErrorType?.deserializeBinary(details.getValue_asU8());
43+
});
44+
45+
return !!errors.find((error) => {
46+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
47+
return error && error instanceof <any>type;
48+
});
49+
}
50+
51+
function isStatusObject(arg: unknown): arg is StatusObject {
1252
if (typeof arg === 'object') {
1353
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1454
const any = arg as any;

0 commit comments

Comments
 (0)