From 941dbad8fa5950b754dde97b02cc4c0ac0e9e0bb Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Tue, 19 Mar 2024 10:39:13 -0700 Subject: [PATCH] feat(common): add isGrpcStatusObjectWithCode user-defined type guard 1. User-defined Typescript type-guard function that asserts whether a value or object is a `@grpc/grpc-js` {Partial} or not. The reason why it checks for {Partial} is because all of the properties of the {StatusObject} are defined as optional for some reason, hence we cannot assume anything about those being present or not by default. 2. Therefore this method will just check if the `code` property is set or not and return `true` or `false` based on that. 3. The above is also the reason why the name of the function is slightly more verbose than your average user-defined type-guard that could be named just "isGrpcStatusObject()" but we wanted to make sure that more specific type-guards can be added later that check for other optional properities or for the presence of all of them together. Link to the status builder within grpc-js: https://github.com/grpc/grpc-node/blob/master/packages/grpc-js/src/status-builder.ts Signed-off-by: Peter Somogyvari --- packages/cactus-common/package.json | 1 + .../grpc/is-grpc-status-object-with-code.ts | 33 +++++++++++++++++++ .../src/main/typescript/public-api.ts | 2 ++ .../is-grpc-status-object-with-code.test.ts | 31 +++++++++++++++++ yarn.lock | 1 + 5 files changed, 68 insertions(+) create mode 100644 packages/cactus-common/src/main/typescript/grpc/is-grpc-status-object-with-code.ts create mode 100644 packages/cactus-common/src/test/typescript/unit/grpc/is-grpc-status-object-with-code.test.ts diff --git a/packages/cactus-common/package.json b/packages/cactus-common/package.json index b92dc56475..4a0e446036 100644 --- a/packages/cactus-common/package.json +++ b/packages/cactus-common/package.json @@ -66,6 +66,7 @@ "sha3": "2.1.4" }, "devDependencies": { + "@grpc/grpc-js": "1.10.3", "@types/json-stable-stringify": "1.0.33", "@types/sanitize-html": "2.9.5", "@types/secp256k1": "4.0.3", diff --git a/packages/cactus-common/src/main/typescript/grpc/is-grpc-status-object-with-code.ts b/packages/cactus-common/src/main/typescript/grpc/is-grpc-status-object-with-code.ts new file mode 100644 index 0000000000..b09fb6cd91 --- /dev/null +++ b/packages/cactus-common/src/main/typescript/grpc/is-grpc-status-object-with-code.ts @@ -0,0 +1,33 @@ +import type { StatusObject } from "@grpc/grpc-js"; + +/** + * User-defined Typescript type-guard function that asserts whether a value or + * object is a `@grpc/grpc-js` {Partial} or not. + * + * The reason why it checks for {Partial} is because all of the properties of + * the {StatusObject} are defined as optional for some reason, hence we cannot + * assume anything about those being present or not by default. + * Therefore this method will just check if the `code` property is set or not + * and return `true` or `false` based on that. + * + * The above is also the reason why the name of the function is slightly more + * verbose than your average user-defined type-guard that could be named just + * "isGrpcStatusObject()" but we wanted to make sure that more specific type-guards + * can be added later that check for other optional properities or for the + * presence of all of them together. + * + * @param x Literally any value or object that will be checked at runtime to have + * the `code` property defined as a number. + * @returns `true` if `x` qualifies, `false` otherwise. + * + * @see {StatusObject} of the @grpc/grpc-js library. + */ +export function isGrpcStatusObjectWithCode( + x: unknown, +): x is Partial { + return ( + !!x && + typeof (x as StatusObject).code === "number" && + isFinite((x as StatusObject).code) + ); +} diff --git a/packages/cactus-common/src/main/typescript/public-api.ts b/packages/cactus-common/src/main/typescript/public-api.ts index 23ab9a9219..18e4ceb6c8 100755 --- a/packages/cactus-common/src/main/typescript/public-api.ts +++ b/packages/cactus-common/src/main/typescript/public-api.ts @@ -45,3 +45,5 @@ export { ExpressHttpVerbMethodName, isExpressHttpVerbMethodName, } from "./http/express-http-verb-method-name"; + +export { isGrpcStatusObjectWithCode } from "./grpc/is-grpc-status-object-with-code"; diff --git a/packages/cactus-common/src/test/typescript/unit/grpc/is-grpc-status-object-with-code.test.ts b/packages/cactus-common/src/test/typescript/unit/grpc/is-grpc-status-object-with-code.test.ts new file mode 100644 index 0000000000..f55186b44c --- /dev/null +++ b/packages/cactus-common/src/test/typescript/unit/grpc/is-grpc-status-object-with-code.test.ts @@ -0,0 +1,31 @@ +import "jest-extended"; +import { isGrpcStatusObjectWithCode } from "../../../../main/typescript"; + +describe("isGrpcStatusObjectWithCode()", () => { + test("returns true for POJO with correct shape and valid data", () => { + expect(isGrpcStatusObjectWithCode({ code: 1 })).toBeTrue(); + expect(isGrpcStatusObjectWithCode({ code: 0 })).toBeTrue(); + expect(isGrpcStatusObjectWithCode({ code: -1 })).toBeTrue(); + }); + + test("returns false for POJO with correct shape and invalid data", () => { + expect(isGrpcStatusObjectWithCode({ code: NaN })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ code: "" })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ code: "Hello" })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ code: true })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ code: [] })).toBeFalse(); + + expect(isGrpcStatusObjectWithCode({ codeX: NaN })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ codeY: "" })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ codeZ: "Hello" })).toBeFalse(); + expect(isGrpcStatusObjectWithCode({ codeK: 1 })).toBeFalse(); + }); + + test("returns false for non-POJO input", () => { + expect(isGrpcStatusObjectWithCode(NaN)).toBeFalse(); + expect(isGrpcStatusObjectWithCode(null)).toBeFalse(); + expect(isGrpcStatusObjectWithCode(undefined)).toBeFalse(); + expect(isGrpcStatusObjectWithCode([])).toBeFalse(); + expect(isGrpcStatusObjectWithCode(Symbol)).toBeFalse(); + }); +}); diff --git a/yarn.lock b/yarn.lock index bf440a423f..51d9f472fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7660,6 +7660,7 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperledger/cactus-common@workspace:packages/cactus-common" dependencies: + "@grpc/grpc-js": "npm:1.10.3" "@types/json-stable-stringify": "npm:1.0.33" "@types/sanitize-html": "npm:2.9.5" "@types/secp256k1": "npm:4.0.3"