From 9e830874dfed51a805566a5bedc62e3d43fc234f Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Wed, 3 Apr 2024 23:25:38 -0700 Subject: [PATCH] feat(cactus-core): add ConnectRPC service interface and type guard Define the types and type guard needed for the API server to be able to recognize plugins that have implemented a ConnectRPC interface for their operations. Also, these types will be used by the plugins themselves to mark the implementations as valid for ConnectRPC usage. ConnectRPC is very similar to gRPC but has some nice features in addition to it such as the HTTP 2 and HTTP 1.1 proxying through express and fastify HTTP server instances. For further details see this link: https://connectrpc.com/ Signed-off-by: Peter Somogyvari --- .cspell.json | 3 + packages/cactus-core-api/package.json | 2 + .../crpc-service/i-plugin-crpc-service.ts | 68 +++++++++++++++++++ .../src/main/typescript/public-api.ts | 4 ++ yarn.lock | 18 +++++ 5 files changed, 95 insertions(+) create mode 100644 packages/cactus-core-api/src/main/typescript/plugin/crpc-service/i-plugin-crpc-service.ts diff --git a/.cspell.json b/.cspell.json index b45f60df1f..1aecf659be 100644 --- a/.cspell.json +++ b/.cspell.json @@ -38,12 +38,14 @@ "clsx", "commenceack", "configtx", + "connectrpc", "Corda", "Cordapp", "couchdb", "COUCHDBADDRESS", "COUCHDBCONFIG", "Creds", + "Crpc", "data", "davecgh", "dclm", @@ -59,6 +61,7 @@ "escc", "execa", "faio", + "fastify", "fidm", "flowdb", "fsouza", diff --git a/packages/cactus-core-api/package.json b/packages/cactus-core-api/package.json index c77d7a3bd9..90733979d1 100644 --- a/packages/cactus-core-api/package.json +++ b/packages/cactus-core-api/package.json @@ -63,6 +63,8 @@ "axios": "1.6.0" }, "devDependencies": { + "@bufbuild/protobuf": "1.8.0", + "@connectrpc/connect": "1.4.0", "@grpc/grpc-js": "1.10.3", "@grpc/proto-loader": "0.7.8", "@types/express": "4.17.19", diff --git a/packages/cactus-core-api/src/main/typescript/plugin/crpc-service/i-plugin-crpc-service.ts b/packages/cactus-core-api/src/main/typescript/plugin/crpc-service/i-plugin-crpc-service.ts new file mode 100644 index 0000000000..fea7c5c8fe --- /dev/null +++ b/packages/cactus-core-api/src/main/typescript/plugin/crpc-service/i-plugin-crpc-service.ts @@ -0,0 +1,68 @@ +import type { ServiceType } from "@bufbuild/protobuf"; +import type { ServiceImpl } from "@connectrpc/connect"; +import type { UniversalHandlerOptions } from "@connectrpc/connect/protocol"; + +/** + * Implementers of this interface are responsible for providing a Crpc service + * that can be dynamically registered at runtime by the API server. + * + * It describes what methods a class (plugin) needs to implement + * in order to be able to operate as a Cacti Crpc service (e.g. expose its + * functionality through Crpc not just HTTP or SocketIO) + * + * @see {IPluginWebService} + * @see {IPluginGrpcService} + * @see {ApiServer} + */ +export interface IPluginCrpcService { + /** + * Used by the API server and/or automated test cases when hooking up this + * plugin instance into a crpc Server object. + * + * The returned pair of service + * definition and implementation objects are passed in to the `.addService()` + * method of the `Server` object of the `@connectrpc/connect` library. + * + * @see {ServiceDefinition} + * @see {ServiceType} + * @see {ICrpcSvcRegistration} + */ + createCrpcSvcRegistrations( + opts: unknown, + ): Promise>>; +} + +/** + * A wrapper object that contains both the the definition and the implementation + * objects of a crpc service that a plugin can implement to expose it's functionality + * over crpc. + * + * @see {IPluginGrpcService} + * @see {IGrpcSvcDefAndImplPair} + */ +export interface ICrpcSvcRegistration { + readonly definition: T; + readonly implementation: Partial>; + readonly serviceName: string; + readonly options?: Partial; +} + +/** + * Custom (user-defined) Typescript type-guard that verifies at runtime whether + * `x` is an implementation of {IPluginCrpcService} or not. + * + * @param x Literally any object or value that you'd want to verify for having + * the right method(s) implemented in-order to qualify as an implementer of the + * {IPluginCrpcService} interface. + * + * The {ApiServer} uses this to filter the total list of plugins down to the ones + * that have crpc services of their own and then hook those up at runtime. + * + * @returns `true` if `x` does implement {IPluginCrpcService} `false` otherwise. + */ +export function isIPluginCrpcService(x: unknown): x is IPluginCrpcService { + return ( + !!x && + typeof (x as IPluginCrpcService).createCrpcSvcRegistrations === "function" + ); +} diff --git a/packages/cactus-core-api/src/main/typescript/public-api.ts b/packages/cactus-core-api/src/main/typescript/public-api.ts index 97ad96a597..7221bf1a6c 100755 --- a/packages/cactus-core-api/src/main/typescript/public-api.ts +++ b/packages/cactus-core-api/src/main/typescript/public-api.ts @@ -48,3 +48,7 @@ export { ISendRequestResultV1 } from "./plugin/ledger-connector/i-send-request-r export { IPluginGrpcService } from "./plugin/grpc-service/i-plugin-grpc-service"; export { IGrpcSvcDefAndImplPair } from "./plugin/grpc-service/i-plugin-grpc-service"; export { isIPluginGrpcService } from "./plugin/grpc-service/i-plugin-grpc-service"; + +export { ICrpcSvcRegistration } from "./plugin/crpc-service/i-plugin-crpc-service"; +export { IPluginCrpcService } from "./plugin/crpc-service/i-plugin-crpc-service"; +export { isIPluginCrpcService } from "./plugin/crpc-service/i-plugin-crpc-service"; diff --git a/yarn.lock b/yarn.lock index f239e4ea79..ce4116c7b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4341,6 +4341,13 @@ __metadata: languageName: node linkType: hard +"@bufbuild/protobuf@npm:1.8.0": + version: 1.8.0 + resolution: "@bufbuild/protobuf@npm:1.8.0" + checksum: 10/f91d60ff1609c023466500e99312d2e92ac09c163d615c315fa25d9e50f1e9b76a3a9cac776786a3dd5c5065bb7061bf29388587e2a1d27306f68ed98e57a892 + languageName: node + linkType: hard + "@chainsafe/as-sha256@npm:^0.3.1": version: 0.3.1 resolution: "@chainsafe/as-sha256@npm:0.3.1" @@ -4607,6 +4614,15 @@ __metadata: languageName: node linkType: hard +"@connectrpc/connect@npm:1.4.0": + version: 1.4.0 + resolution: "@connectrpc/connect@npm:1.4.0" + peerDependencies: + "@bufbuild/protobuf": ^1.4.2 + checksum: 10/5e84fbba544f7e52533bdd0058caaa6d5c89903f522fbd9551c6bad0cd2fea2d3f14c7396696609d1787a5e2018054dd53db40b2d08c55975ff81334074eeeae + languageName: node + linkType: hard + "@cspell/cspell-bundled-dicts@npm:^5.21.2": version: 5.21.2 resolution: "@cspell/cspell-bundled-dicts@npm:5.21.2" @@ -7672,6 +7688,8 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperledger/cactus-core-api@workspace:packages/cactus-core-api" dependencies: + "@bufbuild/protobuf": "npm:1.8.0" + "@connectrpc/connect": "npm:1.4.0" "@grpc/grpc-js": "npm:1.10.3" "@grpc/proto-loader": "npm:0.7.8" "@hyperledger/cactus-common": "npm:2.0.0-alpha.2"