Skip to content

Commit

Permalink
feat: map http status code to grpc status code (#1135)
Browse files Browse the repository at this point in the history
  • Loading branch information
summer-ji-eng authored Nov 16, 2021
1 parent 24b17dd commit 772222f
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/googleError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import {Status} from './status';
import {Status, rpcCodeFromHttpStatusCode} from './status';
import * as protobuf from 'protobufjs';
import {Metadata} from './grpc';

Expand Down Expand Up @@ -63,6 +63,11 @@ export class GoogleError extends Error {
new GoogleError(json['error']['message']),
json.error
);
// Map Http Status Code to gRPC Status Code
if (json['error']['code']) {
error.code = rpcCodeFromHttpStatusCode(json['error']['code']);
}

// Keep consistency with gRPC statusDetails fields. gRPC details has been occupied before.
// Rename "detials" to "statusDetails".
error.statusDetails = json['error']['details'];
Expand Down
35 changes: 35 additions & 0 deletions src/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,38 @@ export enum Status {
DATA_LOSS,
UNAUTHENTICATED,
}

export const HttpCodeToRpcCodeMap = new Map([
[400, Status.INVALID_ARGUMENT],
[401, Status.UNAUTHENTICATED],
[403, Status.PERMISSION_DENIED],
[404, Status.NOT_FOUND],
[409, Status.ABORTED],
[416, Status.OUT_OF_RANGE],
[429, Status.RESOURCE_EXHAUSTED],
[499, Status.CANCELLED],
[501, Status.UNIMPLEMENTED],
[503, Status.UNAVAILABLE],
[504, Status.DEADLINE_EXCEEDED],
]);

// Maps HTTP status codes to gRPC status codes above.
export function rpcCodeFromHttpStatusCode(httpStatusCode: number): number {
if (HttpCodeToRpcCodeMap.has(httpStatusCode)) {
return HttpCodeToRpcCodeMap.get(httpStatusCode)!;
}
// All 2xx
if (httpStatusCode >= 200 && httpStatusCode < 300) {
return Status.OK;
}
// All other 4xx
if (httpStatusCode >= 400 && httpStatusCode < 500) {
return Status.FAILED_PRECONDITION;
}
// All other 5xx
if (httpStatusCode >= 500 && httpStatusCode < 600) {
return Status.INTERNAL;
}
// Everything else
return Status.UNKNOWN;
}
28 changes: 28 additions & 0 deletions test/unit/googleError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as protobuf from 'protobufjs';
import * as path from 'path';
import {GoogleError, GoogleErrorDecoder} from '../../src/googleError';
import {Metadata} from '@grpc/grpc-js';
import {rpcCodeFromHttpStatusCode} from '../../src/status';

interface MyObj {
type: string;
Expand Down Expand Up @@ -273,3 +274,30 @@ describe('parse grpc status details with ErrorInfo from grpc metadata', () => {
assert.strictEqual(decodedError, grpcError);
});
});

describe('map http status code to gRPC status code', () => {
it('error with http status code', () => {
const json = {
error: {
code: 403,
message:
'Cloud Translation API has not been used in project 123 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/translate.googleapis.com/overview?project=455411330361 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.',
status: 'PERMISSION_DENIED',
},
};
const error = GoogleError.parseHttpError(json);
assert.deepStrictEqual(error.code, rpcCodeFromHttpStatusCode(403));
});

it('error without http status code', () => {
const json = {
error: {
message:
'Cloud Translation API has not been used in project 123 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/translate.googleapis.com/overview?project=455411330361 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.',
status: 'PERMISSION_DENIED',
},
};
const error = GoogleError.parseHttpError(json);
assert.deepStrictEqual(error.code, undefined);
});
});
2 changes: 1 addition & 1 deletion test/unit/grpc-fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ describe('grpc-fallback', () => {
JSON.stringify(err.statusDetails),
JSON.stringify(serverError['error']['details'])
);
assert.strictEqual(err.code, serverError['error']['code']);
assert.strictEqual(err.code, 7);
assert.strictEqual(err.message, serverError['error']['message']);
assert.strictEqual(err.reason, errorInfo.reason);
assert.strictEqual(err.domain, errorInfo.domain);
Expand Down
68 changes: 68 additions & 0 deletions test/unit/status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as assert from 'assert';
import {describe, it} from 'mocha';
import {rpcCodeFromHttpStatusCode, Status} from '../../src/status';

describe('status.ts', () => {
it('rpcCodeFromHttpStatusCode', () => {
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(200), Status.OK);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(206), Status.OK);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(300), Status.UNKNOWN);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(307), Status.UNKNOWN);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(400),
Status.INVALID_ARGUMENT
);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(401),
Status.UNAUTHENTICATED
);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(403),
Status.PERMISSION_DENIED
);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(404), Status.NOT_FOUND);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(409), Status.ABORTED);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(416), Status.OUT_OF_RANGE);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(429),
Status.RESOURCE_EXHAUSTED
);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(499), Status.CANCELLED);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(451),
Status.FAILED_PRECONDITION
);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(411),
Status.FAILED_PRECONDITION
);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(501),
Status.UNIMPLEMENTED
);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(503), Status.UNAVAILABLE);
assert.deepStrictEqual(
rpcCodeFromHttpStatusCode(504),
Status.DEADLINE_EXCEEDED
);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(505), Status.INTERNAL);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(510), Status.INTERNAL);
assert.deepStrictEqual(rpcCodeFromHttpStatusCode(100), Status.UNKNOWN);
});
});

0 comments on commit 772222f

Please sign in to comment.