diff --git a/.cspell.json b/.cspell.json index 9891f1deaf..b5329905bf 100644 --- a/.cspell.json +++ b/.cspell.json @@ -36,6 +36,7 @@ "gopath", "grpc", "grpcs", + "grpcwebtext", "hashicorp", "Healthcheck", "HTLC", @@ -71,12 +72,16 @@ "NODETXPOOLACK", "notok", "Oidc", + "oneofs", "onsi", "OpenAPI", "openethereum", "organisation", "parameterizable", "Postgres", + "proto", + "protobuf", + "protoc", "protos", "RUSTC", "Secp", diff --git a/.eslintignore b/.eslintignore index cd9187a309..b1945136b6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,3 +14,5 @@ # **/coverage/** # typings/** + +**/src/main/typescript/generated/proto/** diff --git a/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts b/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts index bb0cee9a75..b668ebb6ed 100644 --- a/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts +++ b/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts @@ -231,6 +231,7 @@ export class CarbonAccountingApp { config.apiHost = addressInfoApi.address; config.cockpitHost = addressInfoCockpit.address; config.cockpitPort = addressInfoCockpit.port; + config.grpcPort = 0; // TODO - make this configurable as well config.logLevel = this.options.logLevel || "INFO"; } diff --git a/examples/cactus-example-carbon-accounting-backend/src/test/typescript/integration/admin-enroll-v1-endpoint.test.ts b/examples/cactus-example-carbon-accounting-backend/src/test/typescript/integration/admin-enroll-v1-endpoint.test.ts index 475e2b42b3..b683746208 100644 --- a/examples/cactus-example-carbon-accounting-backend/src/test/typescript/integration/admin-enroll-v1-endpoint.test.ts +++ b/examples/cactus-example-carbon-accounting-backend/src/test/typescript/integration/admin-enroll-v1-endpoint.test.ts @@ -81,6 +81,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = []; const convictConfig = configService.newExampleConfigConvict(apiSrvOpts); diff --git a/examples/cactus-example-carbon-accounting-frontend/package.json b/examples/cactus-example-carbon-accounting-frontend/package.json index 78297971d9..eece2dd71a 100644 --- a/examples/cactus-example-carbon-accounting-frontend/package.json +++ b/examples/cactus-example-carbon-accounting-frontend/package.json @@ -45,7 +45,6 @@ "@angular/compiler-cli": "12.1.1", "@angular/language-service": "12.1.1", "@ionic/angular-toolkit": "2.3.0", - "@types/jasminewd2": "2.0.3", "@types/node": "12.11.1", "codelyzer": "6.0.2", "https-browserify": "1.0.0", diff --git a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts index ff75b21884..15482dda42 100644 --- a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts +++ b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts @@ -492,6 +492,7 @@ export class SupplyChainApp { properties.apiHost = addressInfoApi.address; properties.cockpitHost = addressInfoCockpit.address; properties.cockpitPort = addressInfoCockpit.port; + properties.grpcPort = 0; // TODO - make this configurable as well properties.logLevel = this.options.logLevel || "INFO"; const apiServer = new ApiServer({ diff --git a/examples/cactus-example-supply-chain-frontend/package.json b/examples/cactus-example-supply-chain-frontend/package.json index dd428d117a..74665c2767 100644 --- a/examples/cactus-example-supply-chain-frontend/package.json +++ b/examples/cactus-example-supply-chain-frontend/package.json @@ -46,7 +46,6 @@ "@angular/compiler-cli": "12.1.1", "@angular/language-service": "12.1.1", "@ionic/angular-toolkit": "2.3.0", - "@types/jasminewd2": "2.0.3", "@types/node": "12.11.1", "codelyzer": "6.0.2", "constants-browserify": "1.0.0", diff --git a/package.json b/package.json index f951f4fc53..5979ca62d1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "start:example-supply-chain": "cd ./examples/supply-chain-app/ && npm i --no-package-lock && npm run start", "start:example-carbon-accounting": "CONFIG_FILE=examples/cactus-example-carbon-accounting-backend/example-config.json node examples/cactus-example-carbon-accounting-backend/dist/lib/main/typescript/carbon-accounting-app-cli.js", "purge-build-cache": "del-cli .build-cache/*", - "clean": "npm run purge-build-cache && del-cli \"./{packages,examples,extensions}/cactus-*/{dist,.nyc_output,src/main/typescript/generated/openapi/typescript-axios/*}\"", + "clean": "npm run purge-build-cache && del-cli \"./{packages,examples,extensions}/cactus-*/{dist,.nyc_output,src/main/proto/generated/*,src/main/typescript/generated/proto/protoc-gen-ts/*,src/main/typescript/generated/openapi/typescript-axios/*}\"", "lint": "eslint '*/*/src/**/*.{js,ts}' --quiet --fix && cspell \"*/*/src/**/*.{js,ts}\"", "tsc": "tsc --build --verbose", "codegen": "lerna run codegen", @@ -78,7 +78,6 @@ "@commitlint/config-conventional": "8.0.0", "@openapitools/openapi-generator-cli": "2.3.3", "@types/fs-extra": "9.0.11", - "@types/jasminewd2": "2.0.10", "@types/node": "15.14.7", "@types/node-fetch": "2.5.4", "@types/tape": "4.13.0", @@ -103,6 +102,8 @@ "fs-extra": "10.0.0", "git-cz": "4.7.6", "globby": "10.0.2", + "grpc-tools": "1.11.2", + "grpc_tools_node_protoc_ts": "5.3.1", "husky": "4.2.5", "inquirer": "8.1.1", "json5": "2.2.0", @@ -117,6 +118,7 @@ "npm-run-all": "4.1.5", "npm-watch": "0.7.0", "prettier": "2.0.5", + "protoc-gen-ts": "0.4.0", "run-time-error": "1.4.0", "secp256k1": "4.0.0", "shebang-loader": "0.0.1", diff --git a/packages/cactus-cmd-api-server/README.md b/packages/cactus-cmd-api-server/README.md index 714e1e7fab..0054e398fb 100644 --- a/packages/cactus-cmd-api-server/README.md +++ b/packages/cactus-cmd-api-server/README.md @@ -116,6 +116,7 @@ const main = async () => { apiServerOptions.apiCorsDomainCsv = "your.domain.example.com"; apiServerOptions.apiPort = 3000; apiServerOptions.cockpitPort = 3100; + apiServerOptions.grpcPort = 5000; // Disble TLS (or provide TLS certs for secure HTTP if you are deploying to production) apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ diff --git a/packages/cactus-cmd-api-server/package.json b/packages/cactus-cmd-api-server/package.json index d5ae8919a2..9f5881696c 100644 --- a/packages/cactus-cmd-api-server/package.json +++ b/packages/cactus-cmd-api-server/package.json @@ -14,6 +14,9 @@ "scripts": { "generate-sdk": "openapi-generator-cli generate -i ./src/main/json/openapi.json -g typescript-axios -o ./src/main/typescript/generated/openapi/typescript-axios/ --reserved-words-mappings protected=protected", "codegen:openapi": "npm run generate-sdk", + "proto:openapi": "openapi-generator-cli generate -i ./src/main/json/openapi.json -g protobuf-schema --model-name-suffix=PB --additional-properties=packageName=org.hyperledger.cactus.cmd_api_server -o ./src/main/proto/generated/openapi/ -t=./src/main/openapi-generator/templates/protobuf-schema/", + "proto:protoc-gen-ts": "yarn run grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/.bin/protoc-gen-ts --ts_out=grpc_js:./src/main/typescript/generated/proto/protoc-gen-ts/ --proto_path ./src/main/proto/generated/openapi/ ./src/main/proto/generated/openapi/services/*.proto", + "codegen:proto": "run-s proto:openapi proto:protoc-gen-ts", "codegen": "run-p 'codegen:*'", "watch": "npm-watch", "webpack": "npm-run-all webpack:dev webpack:prod", @@ -72,6 +75,8 @@ }, "homepage": "https://github.com/hyperledger/cactus#readme", "dependencies": { + "@grpc/grpc-js": "1.3.6", + "@grpc/proto-loader": "0.6.4", "@hyperledger/cactus-common": "0.8.0", "@hyperledger/cactus-core": "0.8.0", "@hyperledger/cactus-core-api": "0.8.0", @@ -109,6 +114,7 @@ "@types/express": "4.17.8", "@types/express-http-proxy": "1.6.1", "@types/express-jwt": "6.0.1", + "@types/google-protobuf": "3.15.3", "@types/jsonwebtoken": "8.5.1", "@types/multer": "1.4.5", "@types/node-forge": "0.9.3", diff --git a/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/api.mustache b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/api.mustache new file mode 100644 index 0000000000..839363b689 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/api.mustache @@ -0,0 +1,46 @@ +{{>partial_header}} +syntax = "proto3"; + +package {{{packageName}}}; + +import "google/protobuf/empty.proto"; +{{#imports}} +{{#import}} +import "{{{modelPackage}}}/{{{.}}}.proto"; +{{/import}} +{{/imports}} + +service {{classname}} { +{{#operations}} +{{#operation}} + {{#description}} + // {{{.}}} + {{/description}} + rpc {{operationId}} ({{#hasParams}}{{operationId}}Request{{/hasParams}}{{^hasParams}}google.protobuf.Empty{{/hasParams}}) returns ({{#vendorExtensions.x-grpc-response}}{{.}}{{/vendorExtensions.x-grpc-response}}{{^vendorExtensions.x-grpc-response}}{{operationId}}Response{{/vendorExtensions.x-grpc-response}}); + +{{/operation}} +{{/operations}} +} + +{{#operations}} +{{#operation}} +{{#hasParams}} +message {{operationId}}Request { + {{#allParams}} + {{#description}} + // {{{.}}} + {{/description}} + {{#vendorExtensions.x-protobuf-type}}{{.}} {{/vendorExtensions.x-protobuf-type}}{{vendorExtensions.x-protobuf-data-type}} {{paramName}} = {{vendorExtensions.x-protobuf-index}}; + {{/allParams}} + +} + +{{/hasParams}} +{{^vendorExtensions.x-grpc-response}} +message {{operationId}}Response { + {{{vendorExtensions.x-grpc-response-type}}} data = 1; +} + +{{/vendorExtensions.x-grpc-response}} +{{/operation}} +{{/operations}} \ No newline at end of file diff --git a/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/model.mustache b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/model.mustache new file mode 100644 index 0000000000..2a9ef1b134 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/model.mustache @@ -0,0 +1,37 @@ +{{>partial_header}} +syntax = "proto3"; + +package {{{packageName}}}; + +{{#imports}} +{{#import}} +import "{{{modelPackage}}}/{{{import}}}.proto"; +{{/import}} +{{/imports}} + +{{#models}} +{{#model}} +message {{classname}} { + {{#vars}} + {{#description}} + // {{{.}}} + {{/description}} + {{^isEnum}} + {{#vendorExtensions.x-protobuf-type}}{{{.}}} {{/vendorExtensions.x-protobuf-type}}{{{vendorExtensions.x-protobuf-data-type}}} {{{name}}} = {{vendorExtensions.x-protobuf-index}}{{#vendorExtensions.x-protobuf-packed}} [packed=true]{{/vendorExtensions.x-protobuf-packed}}; + {{/isEnum}} + {{#isEnum}} + enum {{enumName}} { + {{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{protobuf-enum-index}}}; + {{/enumVars}} + {{/allowableValues}} + } + + {{enumName}} {{name}} = {{vendorExtensions.x-protobuf-index}}; + {{/isEnum}} + + {{/vars}} +} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/root.mustache b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/root.mustache new file mode 100644 index 0000000000..e2df456144 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/openapi-generator/templates/protobuf-schema/root.mustache @@ -0,0 +1,22 @@ +{{>partial_header}} +syntax = "proto3"; + +package {{{packageName}}}; + +{{#vendorExtensions.x-grpc-options}} +option {{{.}}}; +{{/vendorExtensions.x-grpc-options}} + +// Models +{{#models}} +{{#model}} +import "{{modelPackage}}/{{classFilename}}.proto"; +{{/model}} +{{/models}} + +// APIs +{{#apiInfo}} +{{#apis}} +import "{{apiPackage}}/{{classFilename}}.proto"; +{{/apis}} +{{/apiInfo}} \ No newline at end of file diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator-ignore b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/FILES b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/FILES new file mode 100644 index 0000000000..bd08ea9f6f --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/FILES @@ -0,0 +1,6 @@ +.openapi-generator-ignore +README.md +models/health_check_response_pb.proto +models/memory_usage_pb.proto +models/watch_healthcheck_v1_pb.proto +services/default_service.proto diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/VERSION b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/VERSION new file mode 100644 index 0000000000..3bff059174 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.1.1 \ No newline at end of file diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/README.md b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/README.md new file mode 100644 index 0000000000..e8f8c7b355 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/README.md @@ -0,0 +1,31 @@ +# gPRC for org.hyperledger.cactus.cmd_api_server + +Interact with a Cactus deployment through HTTP. + +## Overview +These files were generated by the [OpenAPI Generator](https://openapi-generator.tech) project. + +- API version: 0.0.1 +- Package version: +- Build package: org.openapitools.codegen.languages.ProtobufSchemaCodegen + +## Usage + +Below are some usage examples for Go and Ruby. For other languages, please refer to https://grpc.io/docs/quickstart/. + +### Go +``` +# assuming `protoc-gen-go` has been installed with `go get -u github.com/golang/protobuf/protoc-gen-go` +mkdir /var/tmp/go/ +protoc --go_out=/var/tmp/go/ services/* +protoc --go_out=/var/tmp/go/ models/* +``` + +### Ruby +``` +# assuming `grpc_tools_ruby_protoc` has been installed via `gem install grpc-tools` +RUBY_OUTPUT_DIR="/var/tmp/ruby/org.hyperledger.cactus.cmd_api_server" +mkdir $RUBY_OUTPUT_DIR +grpc_tools_ruby_protoc --ruby_out=$RUBY_OUTPUT_DIR --grpc_out=$RUBY_OUTPUT_DIR/lib services/* +grpc_tools_ruby_protoc --ruby_out=$RUBY_OUTPUT_DIR --grpc_out=$RUBY_OUTPUT_DIR/lib models/* +``` diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/health_check_response_pb.proto b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/health_check_response_pb.proto new file mode 100644 index 0000000000..2a4fc3b992 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/health_check_response_pb.proto @@ -0,0 +1,24 @@ +/* + Hyperledger Cactus API + + Interact with a Cactus deployment through HTTP. + + The version of the OpenAPI document: 0.0.1 + + Generated by OpenAPI Generator: https://openapi-generator.tech +*/ + +syntax = "proto3"; + +package org.hyperledger.cactus.cmd_api_server; + +import "models/memory_usage_pb.proto"; + +message HealthCheckResponsePB { + bool success = 256557056; + + string createdAt = 61500732; + + MemoryUsagePB memoryUsage = 335792418; + +} diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/memory_usage_pb.proto b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/memory_usage_pb.proto new file mode 100644 index 0000000000..0124db27e7 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/memory_usage_pb.proto @@ -0,0 +1,27 @@ +/* + Hyperledger Cactus API + + Interact with a Cactus deployment through HTTP. + + The version of the OpenAPI document: 0.0.1 + + Generated by OpenAPI Generator: https://openapi-generator.tech +*/ + +syntax = "proto3"; + +package org.hyperledger.cactus.cmd_api_server; + + +message MemoryUsagePB { + float rss = 113234; + + float heapTotal = 114487480; + + float heapUsed = 30910521; + + float external = 210148408; + + float arrayBuffers = 116952168; + +} diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/watch_healthcheck_v1_pb.proto b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/watch_healthcheck_v1_pb.proto new file mode 100644 index 0000000000..51fc928a96 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/models/watch_healthcheck_v1_pb.proto @@ -0,0 +1,17 @@ +/* + Hyperledger Cactus API + + Interact with a Cactus deployment through HTTP. + + The version of the OpenAPI document: 0.0.1 + + Generated by OpenAPI Generator: https://openapi-generator.tech +*/ + +syntax = "proto3"; + +package org.hyperledger.cactus.cmd_api_server; + + +message WatchHealthcheckV1PB { +} diff --git a/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/services/default_service.proto b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/services/default_service.proto new file mode 100644 index 0000000000..59155057a7 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/proto/generated/openapi/services/default_service.proto @@ -0,0 +1,28 @@ +/* + Hyperledger Cactus API + + Interact with a Cactus deployment through HTTP. + + The version of the OpenAPI document: 0.0.1 + + Generated by OpenAPI Generator: https://openapi-generator.tech +*/ + +syntax = "proto3"; + +package org.hyperledger.cactus.cmd_api_server; + +import "google/protobuf/empty.proto"; +import "models/health_check_response_pb.proto"; + +service DefaultService { + rpc GetHealthCheckV1 (google.protobuf.Empty) returns (HealthCheckResponsePB); + + rpc GetPrometheusMetricsV1 (google.protobuf.Empty) returns (GetPrometheusMetricsV1Response); + +} + +message GetPrometheusMetricsV1Response { + string data = 1; +} + diff --git a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts index ec25467f3b..6bcf79f9cf 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts @@ -5,10 +5,13 @@ import path from "path"; import tls from "tls"; import { Server, createServer } from "http"; import { createServer as createSecureServer } from "https"; +import { RuntimeError } from "run-time-error"; import { gte } from "semver"; import lmify from "lmify"; import fs from "fs-extra"; import expressHttpProxy from "express-http-proxy"; +import { Server as GrpcServer } from "@grpc/grpc-js"; +import { ServerCredentials as GrpcServerCredentials } from "@grpc/grpc-js"; import type { Application, Request, Response, RequestHandler } from "express"; import express from "express"; import { OpenApiValidator } from "express-openapi-validator"; @@ -43,16 +46,20 @@ import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter"; import { AuthorizerFactory } from "./authzn/authorizer-factory"; import { WatchHealthcheckV1 } from "./generated/openapi/typescript-axios"; import { WatchHealthcheckV1Endpoint } from "./web-services/watch-healthcheck-v1-endpoint"; -import { RuntimeError } from "run-time-error"; +import * as default_service from "./generated/proto/protoc-gen-ts/services/default_service"; +import { GrpcServerApiServer } from "./web-services/grpc/grpc-server-api-server"; +import { determineAddressFamily } from "./common/determine-address-family"; + export interface IApiServerConstructorOptions { - pluginManagerOptions?: { pluginsPath: string }; - pluginRegistry?: PluginRegistry; - httpServerApi?: Server | SecureServer; - wsServerApi?: SocketIoServer; - wsOptions?: SocketIoServerOptions; - httpServerCockpit?: Server | SecureServer; - config: ICactusApiServerOptions; - prometheusExporter?: PrometheusExporter; + readonly pluginManagerOptions?: { pluginsPath: string }; + readonly pluginRegistry?: PluginRegistry; + readonly httpServerApi?: Server | SecureServer; + readonly wsServerApi?: SocketIoServer; + readonly grpcServer?: GrpcServer; + readonly wsOptions?: SocketIoServerOptions; + readonly httpServerCockpit?: Server | SecureServer; + readonly config: ICactusApiServerOptions; + readonly prometheusExporter?: PrometheusExporter; } export class ApiServer { @@ -73,6 +80,7 @@ export class ApiServer { private readonly httpServerApi: Server | SecureServer; private readonly httpServerCockpit: Server | SecureServer; private readonly wsApi: SocketIoServer; + private readonly grpcServer: GrpcServer; private readonly expressApi: Application; private readonly expressCockpit: Application; private readonly pluginsPath: string; @@ -114,6 +122,7 @@ export class ApiServer { this.httpServerCockpit = createServer(); } + this.grpcServer = this.options.grpcServer || new GrpcServer({}); this.wsApi = new SocketIoServer(); this.expressApi = express(); this.expressCockpit = express(); @@ -169,6 +178,7 @@ export class ApiServer { async start(): Promise<{ addressInfoCockpit: AddressInfo; addressInfoApi: AddressInfo; + addressInfoGrpc: AddressInfo; }> { this.checkNodeVersion(); const tlsMaxVersion = this.options.config.tlsDefaultMaxVersion; @@ -179,6 +189,13 @@ export class ApiServer { const { cockpitTlsEnabled, apiTlsEnabled } = this.options.config; const addressInfoCockpit = await this.startCockpitFileServer(); const addressInfoApi = await this.startApiServer(); + const addressInfoGrpc = await this.startGrpcServer(); + + { + const { port, address } = addressInfoGrpc; + const grpcUrl = `${address}:${port}`; + this.log.info(`Cactus gRPC reachable ${grpcUrl}`); + } { const { apiHost: host } = this.options.config; @@ -196,7 +213,7 @@ export class ApiServer { this.log.info(`Cactus Cockpit reachable ${httpUrl}`); } - return { addressInfoCockpit, addressInfoApi }; + return { addressInfoCockpit, addressInfoApi, addressInfoGrpc }; } catch (ex) { const errorMessage = `Failed to start ApiServer: ${ex.stack}`; this.log.error(errorMessage); @@ -368,6 +385,21 @@ export class ApiServer { await Servers.shutdown(this.httpServerCockpit); this.log.info(`Close HTTP server of the cockpit OK`); } + + if (this.grpcServer) { + this.log.info(`Closing gRPC server ...`); + await new Promise((resolve, reject) => { + this.grpcServer.tryShutdown((ex?: Error) => { + if (ex) { + this.log.error("Failed to shut down gRPC server: ", ex); + reject(ex); + } else { + resolve(); + } + }); + }); + this.log.info(`Close gRPC server OK`); + } } async startCockpitFileServer(): Promise { @@ -501,6 +533,45 @@ export class ApiServer { ); } + async startGrpcServer(): Promise { + return new Promise((resolve, reject) => { + // const grpcHost = "0.0.0.0"; // FIXME - make this configurable (config-service.ts) + const grpcHost = "127.0.0.1"; // FIXME - make this configurable (config-service.ts) + const grpcHostAndPort = `${grpcHost}:${this.options.config.grpcPort}`; + + const grpcTlsCredentials = this.options.config.grpcMtlsEnabled + ? GrpcServerCredentials.createSsl( + Buffer.from(this.options.config.apiTlsCertPem), + [ + { + cert_chain: Buffer.from(this.options.config.apiTlsCertPem), + private_key: Buffer.from(this.options.config.apiTlsKeyPem), + }, + ], + true, + ) + : GrpcServerCredentials.createInsecure(); + + this.grpcServer.bindAsync( + grpcHostAndPort, + grpcTlsCredentials, + (error: Error | null, port: number) => { + if (error) { + return reject(new RuntimeError("Binding gRPC failed: ", error)); + } + this.grpcServer.addService( + default_service.org.hyperledger.cactus.cmd_api_server + .UnimplementedDefaultServiceService.definition, + new GrpcServerApiServer(), + ); + this.grpcServer.start(); + const family = determineAddressFamily(grpcHost); + resolve({ address: grpcHost, port, family }); + }, + ); + }); + } + async startApiServer(): Promise { const { options, expressApi: app, wsApi } = this; const { config } = options; diff --git a/packages/cactus-cmd-api-server/src/main/typescript/common/determine-address-family.ts b/packages/cactus-cmd-api-server/src/main/typescript/common/determine-address-family.ts new file mode 100644 index 0000000000..12a6cc857d --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/common/determine-address-family.ts @@ -0,0 +1,13 @@ +import { isIPv4, isIPv6 } from "net"; + +export function determineAddressFamily( + address: string, +): "IPv4" | "IPv6" | "IPvUnknown" { + if (isIPv4(address)) { + return "IPv4"; + } else if (isIPv6(address)) { + return "IPv6"; + } else { + return "IPvUnknown"; + } +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts index 25a6e632bf..a37692f1b5 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts @@ -52,6 +52,8 @@ export interface ICactusApiServerOptions { apiTlsCertPem: string; apiTlsKeyPem: string; apiTlsClientCaPem: string; + grpcPort: number; + grpcMtlsEnabled: boolean; plugins: PluginImport[]; keyPairPem: string; keychainSuffixKeyPairPem: string; @@ -357,6 +359,22 @@ export class ConfigService { arg: "api-tls-key-pem", default: null as string | null, }, + grpcPort: { + doc: "The gRPC port to serve web services on.", + format: "port", + env: "GRPC_PORT", + arg: "grpc-port", + default: 5000, + }, + grpcMtlsEnabled: { + doc: + "Enable TLS termination on the grpc server. Useful if you do not have/want to " + + "have a reverse proxy or load balancer doing the SSL/TLS termination in your environment.", + format: Boolean, + env: "GRPC_TLS_ENABLED", + arg: "grpc-tls-enabled", + default: true, + }, keyPairPem: { sensitive: true, doc: @@ -445,6 +463,8 @@ export class ConfigService { const apiPort = (schema.apiPort as SchemaObj).default; const apiProtocol = apiTlsEnabled ? "https:" : "http"; const apiBaseUrl = `${apiProtocol}//${apiHost}:${apiPort}`; + const grpcPort = (schema.grpcPort as SchemaObj).default; + const grpcMtlsEnabled = (schema.grpcMtlsEnabled as SchemaObj).default; const keyPair = JWK.generateSync("EC", "secp256k1", { use: "sig" }, true); const keyPairPem = keyPair.toPEM(true); @@ -545,6 +565,8 @@ export class ConfigService { apiTlsCertPem: pkiServer.certificatePem, apiTlsKeyPem: pkiServer.privateKeyPem, apiTlsClientCaPem: "-", // API mTLS is off so this will not crash the server + grpcPort, + grpcMtlsEnabled, cockpitHost, cockpitPort, cockpitWwwRoot: (schema.cockpitWwwRoot as SchemaObj).default, diff --git a/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/google/protobuf/empty.ts b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/google/protobuf/empty.ts new file mode 100644 index 0000000000..b7e69f9aaa --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/google/protobuf/empty.ts @@ -0,0 +1,45 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 3.15.6 + * source: google/protobuf/empty.proto + * git: https://github.com/thesayyn/protoc-gen-ts + * buymeacoffee: https://www.buymeacoffee.com/thesayyn + * */ +import * as pb_1 from "google-protobuf"; +export namespace google.protobuf { + export class Empty extends pb_1.Message { + constructor(data?: any[] | {}) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], []); + if (!Array.isArray(data) && typeof data == "object") { } + } + toObject() { + const data: {} = {}; + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Empty { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Empty(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Empty { + return Empty.deserialize(bytes); + } + } +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/health_check_response_pb.ts b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/health_check_response_pb.ts new file mode 100644 index 0000000000..14065d3222 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/health_check_response_pb.ts @@ -0,0 +1,106 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 3.15.6 + * source: models/health_check_response_pb.proto + * git: https://github.com/thesayyn/protoc-gen-ts + * buymeacoffee: https://www.buymeacoffee.com/thesayyn + * */ +import * as dependency_1 from "./memory_usage_pb"; +import * as pb_1 from "google-protobuf"; +export namespace org.hyperledger.cactus.cmd_api_server { + export class HealthCheckResponsePB extends pb_1.Message { + constructor(data?: any[] | { + success?: boolean; + createdAt?: string; + memoryUsage?: dependency_1.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], []); + if (!Array.isArray(data) && typeof data == "object") { + if ("success" in data && data.success != undefined) { + this.success = data.success; + } + if ("createdAt" in data && data.createdAt != undefined) { + this.createdAt = data.createdAt; + } + if ("memoryUsage" in data && data.memoryUsage != undefined) { + this.memoryUsage = data.memoryUsage; + } + } + } + get success() { + return pb_1.Message.getField(this, 256557056) as boolean; + } + set success(value: boolean) { + pb_1.Message.setField(this, 256557056, value); + } + get createdAt() { + return pb_1.Message.getField(this, 61500732) as string; + } + set createdAt(value: string) { + pb_1.Message.setField(this, 61500732, value); + } + get memoryUsage() { + return pb_1.Message.getWrapperField(this, dependency_1.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB, 335792418) as dependency_1.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB; + } + set memoryUsage(value: dependency_1.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB) { + pb_1.Message.setWrapperField(this, 335792418, value); + } + toObject() { + const data: { + success?: boolean; + createdAt?: string; + memoryUsage?: ReturnType; + } = {}; + if (this.success != null) { + data.success = this.success; + } + if (this.createdAt != null) { + data.createdAt = this.createdAt; + } + if (this.memoryUsage != null) { + data.memoryUsage = this.memoryUsage.toObject(); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.success !== undefined) + writer.writeBool(256557056, this.success); + if (typeof this.createdAt === "string" && this.createdAt.length) + writer.writeString(61500732, this.createdAt); + if (this.memoryUsage !== undefined) + writer.writeMessage(335792418, this.memoryUsage, () => this.memoryUsage.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): HealthCheckResponsePB { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new HealthCheckResponsePB(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 256557056: + message.success = reader.readBool(); + break; + case 61500732: + message.createdAt = reader.readString(); + break; + case 335792418: + reader.readMessage(message.memoryUsage, () => message.memoryUsage = dependency_1.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB.deserialize(reader)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): HealthCheckResponsePB { + return HealthCheckResponsePB.deserialize(bytes); + } + } +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/memory_usage_pb.ts b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/memory_usage_pb.ts new file mode 100644 index 0000000000..ee17b4c371 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/memory_usage_pb.ts @@ -0,0 +1,143 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 3.15.6 + * source: models/memory_usage_pb.proto + * git: https://github.com/thesayyn/protoc-gen-ts + * buymeacoffee: https://www.buymeacoffee.com/thesayyn + * */ +import * as pb_1 from "google-protobuf"; +export namespace org.hyperledger.cactus.cmd_api_server { + export class MemoryUsagePB extends pb_1.Message { + constructor(data?: any[] | { + rss?: number; + heapTotal?: number; + heapUsed?: number; + external?: number; + arrayBuffers?: number; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], []); + if (!Array.isArray(data) && typeof data == "object") { + if ("rss" in data && data.rss != undefined) { + this.rss = data.rss; + } + if ("heapTotal" in data && data.heapTotal != undefined) { + this.heapTotal = data.heapTotal; + } + if ("heapUsed" in data && data.heapUsed != undefined) { + this.heapUsed = data.heapUsed; + } + if ("external" in data && data.external != undefined) { + this.external = data.external; + } + if ("arrayBuffers" in data && data.arrayBuffers != undefined) { + this.arrayBuffers = data.arrayBuffers; + } + } + } + get rss() { + return pb_1.Message.getField(this, 113234) as number; + } + set rss(value: number) { + pb_1.Message.setField(this, 113234, value); + } + get heapTotal() { + return pb_1.Message.getField(this, 114487480) as number; + } + set heapTotal(value: number) { + pb_1.Message.setField(this, 114487480, value); + } + get heapUsed() { + return pb_1.Message.getField(this, 30910521) as number; + } + set heapUsed(value: number) { + pb_1.Message.setField(this, 30910521, value); + } + get external() { + return pb_1.Message.getField(this, 210148408) as number; + } + set external(value: number) { + pb_1.Message.setField(this, 210148408, value); + } + get arrayBuffers() { + return pb_1.Message.getField(this, 116952168) as number; + } + set arrayBuffers(value: number) { + pb_1.Message.setField(this, 116952168, value); + } + toObject() { + const data: { + rss?: number; + heapTotal?: number; + heapUsed?: number; + external?: number; + arrayBuffers?: number; + } = {}; + if (this.rss != null) { + data.rss = this.rss; + } + if (this.heapTotal != null) { + data.heapTotal = this.heapTotal; + } + if (this.heapUsed != null) { + data.heapUsed = this.heapUsed; + } + if (this.external != null) { + data.external = this.external; + } + if (this.arrayBuffers != null) { + data.arrayBuffers = this.arrayBuffers; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.rss !== undefined) + writer.writeFloat(113234, this.rss); + if (this.heapTotal !== undefined) + writer.writeFloat(114487480, this.heapTotal); + if (this.heapUsed !== undefined) + writer.writeFloat(30910521, this.heapUsed); + if (this.external !== undefined) + writer.writeFloat(210148408, this.external); + if (this.arrayBuffers !== undefined) + writer.writeFloat(116952168, this.arrayBuffers); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): MemoryUsagePB { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new MemoryUsagePB(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 113234: + message.rss = reader.readFloat(); + break; + case 114487480: + message.heapTotal = reader.readFloat(); + break; + case 30910521: + message.heapUsed = reader.readFloat(); + break; + case 210148408: + message.external = reader.readFloat(); + break; + case 116952168: + message.arrayBuffers = reader.readFloat(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): MemoryUsagePB { + return MemoryUsagePB.deserialize(bytes); + } + } +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/services/default_service.ts b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/services/default_service.ts new file mode 100644 index 0000000000..de628e92b3 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/services/default_service.ts @@ -0,0 +1,114 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 3.15.6 + * source: services/default_service.proto + * git: https://github.com/thesayyn/protoc-gen-ts + * buymeacoffee: https://www.buymeacoffee.com/thesayyn + * */ +import * as dependency_1 from "./../google/protobuf/empty"; +import * as dependency_2 from "./../models/health_check_response_pb"; +import * as pb_1 from "google-protobuf"; +import * as grpc_1 from "@grpc/grpc-js"; +export namespace org.hyperledger.cactus.cmd_api_server { + export class GetPrometheusMetricsV1Response extends pb_1.Message { + constructor(data?: any[] | { + data?: string; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], []); + if (!Array.isArray(data) && typeof data == "object") { + if ("data" in data && data.data != undefined) { + this.data = data.data; + } + } + } + get data() { + return pb_1.Message.getField(this, 1) as string; + } + set data(value: string) { + pb_1.Message.setField(this, 1, value); + } + toObject() { + const data: { + data?: string; + } = {}; + if (this.data != null) { + data.data = this.data; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (typeof this.data === "string" && this.data.length) + writer.writeString(1, this.data); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): GetPrometheusMetricsV1Response { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new GetPrometheusMetricsV1Response(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + message.data = reader.readString(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): GetPrometheusMetricsV1Response { + return GetPrometheusMetricsV1Response.deserialize(bytes); + } + } + export abstract class UnimplementedDefaultServiceService { + static definition = { + GetHealthCheckV1: { + path: "/org.hyperledger.cactus.cmd_api_server.DefaultService/GetHealthCheckV1", + requestStream: false, + responseStream: false, + requestSerialize: (message: dependency_1.google.protobuf.Empty) => Buffer.from(message.serialize()), + requestDeserialize: (bytes: Buffer) => dependency_1.google.protobuf.Empty.deserialize(new Uint8Array(bytes)), + responseSerialize: (message: dependency_2.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB) => Buffer.from(message.serialize()), + responseDeserialize: (bytes: Buffer) => dependency_2.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB.deserialize(new Uint8Array(bytes)) + }, + GetPrometheusMetricsV1: { + path: "/org.hyperledger.cactus.cmd_api_server.DefaultService/GetPrometheusMetricsV1", + requestStream: false, + responseStream: false, + requestSerialize: (message: dependency_1.google.protobuf.Empty) => Buffer.from(message.serialize()), + requestDeserialize: (bytes: Buffer) => dependency_1.google.protobuf.Empty.deserialize(new Uint8Array(bytes)), + responseSerialize: (message: GetPrometheusMetricsV1Response) => Buffer.from(message.serialize()), + responseDeserialize: (bytes: Buffer) => GetPrometheusMetricsV1Response.deserialize(new Uint8Array(bytes)) + } + }; + [method: string]: grpc_1.UntypedHandleCall; + abstract GetHealthCheckV1(call: grpc_1.ServerUnaryCall, callback: grpc_1.requestCallback): void; + abstract GetPrometheusMetricsV1(call: grpc_1.ServerUnaryCall, callback: grpc_1.requestCallback): void; + } + export class DefaultServiceClient extends grpc_1.makeGenericClientConstructor(UnimplementedDefaultServiceService.definition, "DefaultService", {}) { + constructor(address: string, credentials: grpc_1.ChannelCredentials, options?: Partial) { + super(address, credentials, options) + } + GetHealthCheckV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata, options: grpc_1.CallOptions, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetHealthCheckV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetHealthCheckV1(message: dependency_1.google.protobuf.Empty, options: grpc_1.CallOptions, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetHealthCheckV1(message: dependency_1.google.protobuf.Empty, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetHealthCheckV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback, options?: grpc_1.CallOptions | grpc_1.requestCallback, callback?: grpc_1.requestCallback): grpc_1.ClientUnaryCall { + return super.GetHealthCheckV1(message, metadata, options, callback); + } + GetPrometheusMetricsV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata, options: grpc_1.CallOptions, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetPrometheusMetricsV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetPrometheusMetricsV1(message: dependency_1.google.protobuf.Empty, options: grpc_1.CallOptions, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetPrometheusMetricsV1(message: dependency_1.google.protobuf.Empty, callback: grpc_1.requestCallback): grpc_1.ClientUnaryCall; + GetPrometheusMetricsV1(message: dependency_1.google.protobuf.Empty, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback, options?: grpc_1.CallOptions | grpc_1.requestCallback, callback?: grpc_1.requestCallback): grpc_1.ClientUnaryCall { + return super.GetPrometheusMetricsV1(message, metadata, options, callback); + } + } +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/proto.d.ts b/packages/cactus-cmd-api-server/src/main/typescript/proto.d.ts new file mode 100644 index 0000000000..770637e29e --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/proto.d.ts @@ -0,0 +1,5 @@ + +declare module '*.proto' { + const fileContent: string; + export default fileContent; +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/public-api.ts b/packages/cactus-cmd-api-server/src/main/typescript/public-api.ts index f2fdad000b..34a44578a1 100755 --- a/packages/cactus-cmd-api-server/src/main/typescript/public-api.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/public-api.ts @@ -16,6 +16,13 @@ export { IPki, } from "./config/self-signed-pki-generator"; +// gRPC - generated models and client +export * as default_service from "./generated/proto/protoc-gen-ts/services/default_service"; +export * as health_check_response_pb from "./generated/proto/protoc-gen-ts/models/health_check_response_pb"; +export * as memory_usage_pb from "./generated/proto/protoc-gen-ts/models/memory_usage_pb"; +export * as empty from "./generated/proto/protoc-gen-ts/google/protobuf/empty"; + +// HTTP - generated models and client export * from "./generated/openapi/typescript-axios/index"; export { ApiServerApiClient } from "./api-client/api-server-api-client"; diff --git a/packages/cactus-cmd-api-server/src/main/typescript/web-services/grpc/grpc-server-api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/web-services/grpc/grpc-server-api-server.ts new file mode 100644 index 0000000000..b6ae3f2063 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/web-services/grpc/grpc-server-api-server.ts @@ -0,0 +1,45 @@ +import { ServerUnaryCall, requestCallback } from "@grpc/grpc-js"; +import { Empty } from "google-protobuf/google/protobuf/empty_pb"; + +import * as health_check_response_pb from "../../generated/proto/protoc-gen-ts/models/health_check_response_pb"; +import * as memory_usage_pb from "../../generated/proto/protoc-gen-ts/models/memory_usage_pb"; +import * as default_service from "../../generated/proto/protoc-gen-ts/services/default_service"; + +export class GrpcServerApiServer extends default_service.org.hyperledger.cactus + .cmd_api_server.UnimplementedDefaultServiceService { + GetHealthCheckV1( + call: ServerUnaryCall< + Empty, + health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB + >, + callback: requestCallback< + health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB + >, + ): void { + const memoryUsage = new memory_usage_pb.org.hyperledger.cactus.cmd_api_server.MemoryUsagePB( + process.memoryUsage(), + ); + + const healthCheckResponse = new health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB( + { + success: true, + createdAt: new Date().toJSON(), + memoryUsage, + }, + ); + callback(null, healthCheckResponse); + } + + GetPrometheusMetricsV1( + call: ServerUnaryCall< + Empty, + default_service.org.hyperledger.cactus.cmd_api_server.GetPrometheusMetricsV1Response + >, + callback: requestCallback< + default_service.org.hyperledger.cactus.cmd_api_server.GetPrometheusMetricsV1Response + >, + ): void { + const res = new default_service.org.hyperledger.cactus.cmd_api_server.GetPrometheusMetricsV1Response(); + callback(null, res); + } +} diff --git a/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts index 16e4393ab0..ad1273b04d 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts @@ -64,6 +64,7 @@ test("Start API server, and run Artillery benchmark test.", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 4000; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.logLevel = "info"; apiServerOptions.plugins = [ diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts index 1bafbf5cd2..8d426b95e6 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts @@ -82,6 +82,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = [ { diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authz-scope-enforcement.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authz-scope-enforcement.test.ts index 539e143765..1bb8aa70ce 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authz-scope-enforcement.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authz-scope-enforcement.test.ts @@ -67,6 +67,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = []; const config = configService.newExampleConfigConvict(apiSrvOpts); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts index 140e0c1c87..bbc3dc2c61 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-socketio-endpoint-authorization.test.ts @@ -55,6 +55,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = []; const config = configService.newExampleConfigConvict(apiSrvOpts); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz-ops-confirm.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz-ops-confirm.test.ts index 1bebde1a90..bf1ab283d1 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz-ops-confirm.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz-ops-confirm.test.ts @@ -57,6 +57,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = []; const config = configService.newExampleConfigConvict(apiSrvOpts); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz.test.ts index 190d44bf40..e7cc425912 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-unprotected-endpoint-authz.test.ts @@ -65,6 +65,7 @@ test(testCase, async (t: Test) => { apiSrvOpts.apiCorsDomainCsv = "*"; apiSrvOpts.apiPort = 0; apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcPort = 0; apiSrvOpts.apiTlsEnabled = false; apiSrvOpts.plugins = []; const config = configService.newExampleConfigConvict(apiSrvOpts); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts index cf7935ead7..8cf1d41ee2 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts @@ -74,6 +74,7 @@ test("NodeJS API server + Rust plugin work together", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ { diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-js-proto-loader-client-healthcheck.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-js-proto-loader-client-healthcheck.test.ts new file mode 100644 index 0000000000..efd15b8f60 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-js-proto-loader-client-healthcheck.test.ts @@ -0,0 +1,105 @@ +import test, { Test } from "tape-promise/tape"; +import path from "path"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { + ApiServer, + ConfigService, + HealthCheckResponse, +} from "../../../main/typescript/public-api"; +import { AuthorizationProtocol } from "../../../main/typescript/public-api"; +import { ServiceClientConstructor } from "@grpc/grpc-js/build/src/make-client"; +import * as grpc from "@grpc/grpc-js"; +import * as protoLoader from "@grpc/proto-loader"; +import { Empty } from "google-protobuf/google/protobuf/empty_pb"; + +const testCase = "API server: runs gRPC web services - proto loader"; +const logLevel: LogLevelDesc = "TRACE"; + +test(testCase, async (t: Test) => { + const configService = new ConfigService(); + const apiSrvOpts = configService.newExampleConfig(); + apiSrvOpts.authorizationProtocol = AuthorizationProtocol.NONE; + apiSrvOpts.configFile = ""; + apiSrvOpts.logLevel = logLevel; + apiSrvOpts.apiCorsDomainCsv = "*"; + apiSrvOpts.apiPort = 0; + apiSrvOpts.grpcPort = 0; + apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcMtlsEnabled = false; + apiSrvOpts.apiTlsEnabled = false; + apiSrvOpts.plugins = []; + const config = configService.newExampleConfigConvict(apiSrvOpts); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + test.onFinish(async () => await apiServer.shutdown()); + + const startResponse = apiServer.start(); + await t.doesNotReject(startResponse, "start API server OK"); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoApi = (await startResponse).addressInfoGrpc; + const { address, port } = addressInfoApi; + const grpcHostAndPort = `${address}:${port}`; + t.ok(grpcHostAndPort, "grpcHostAndPort truthy OK"); + + const PROTO_PATH = path.join( + __dirname, + "../../../main/proto/generated/openapi/services/default_service.proto", + ); + + const PROTO_INCLUDE_DIR = path.join( + __dirname, + "../../../main/proto/generated/openapi/", + ); + + const packageDefinition = await protoLoader.load(PROTO_PATH, { + includeDirs: [PROTO_INCLUDE_DIR], + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, + }); + + const grpcPkg = grpc.loadPackageDefinition(packageDefinition); + t.ok(grpcPkg, "grpcPkg truthy OK"); + + const DefaultService: ServiceClientConstructor = (grpcPkg as any).org + .hyperledger.cactus.cmd_api_server.DefaultService; + + t.ok(DefaultService, "DefaultService truthy OK"); + + const client = new DefaultService( + grpcHostAndPort, + grpc.credentials.createInsecure(), + ); + t.ok(client, "proto loaded client truthy OK"); + + const request = new Empty(); + + const res1 = await new Promise((resolve, reject) => { + client.getHealthCheckV1( + request, + (err: grpc.ServiceError | null, value: HealthCheckResponse) => { + if (err) { + reject(err); + } else { + resolve(value); + } + }, + ); + }); + t.ok(res1, "res1 truthy OK"); + t.ok(res1.createdAt, "res1.createdAt truthy OK"); + t.ok(res1.memoryUsage, "res1.memoryUsage truthy OK"); + t.ok(res1.memoryUsage.heapTotal, "res1.memoryUsage.heapTotal truthy OK"); + t.ok(res1.memoryUsage.heapUsed, "res1.memoryUsage.heapUsed truthy OK"); + t.ok(res1.memoryUsage.rss, "res1.memoryUsage.rss truthy OK"); + t.true(res1.success, "res1.success true OK"); + + t.end(); +}); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-healthcheck.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-healthcheck.test.ts new file mode 100644 index 0000000000..65f5da2133 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-healthcheck.test.ts @@ -0,0 +1,85 @@ +import test, { Test } from "tape-promise/tape"; + +import * as grpc from "@grpc/grpc-js"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { ApiServer, ConfigService } from "../../../main/typescript/public-api"; +import { AuthorizationProtocol } from "../../../main/typescript/public-api"; +import { default_service } from "../../../main/typescript/public-api"; +import { health_check_response_pb } from "../../../main/typescript/public-api"; +import { empty } from "../../../main/typescript/public-api"; +import { RuntimeError } from "run-time-error"; + +const testCase = "API server: runs gRPC TS-proto web services"; +const logLevel: LogLevelDesc = "TRACE"; + +test(testCase, async (t: Test) => { + const configService = new ConfigService(); + const apiSrvOpts = configService.newExampleConfig(); + apiSrvOpts.authorizationProtocol = AuthorizationProtocol.NONE; + apiSrvOpts.configFile = ""; + apiSrvOpts.logLevel = logLevel; + apiSrvOpts.apiCorsDomainCsv = "*"; + apiSrvOpts.apiPort = 0; + apiSrvOpts.grpcPort = 0; + apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcMtlsEnabled = false; + apiSrvOpts.apiTlsEnabled = false; + apiSrvOpts.plugins = []; + const config = configService.newExampleConfigConvict(apiSrvOpts); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + test.onFinish(async () => await apiServer.shutdown()); + + const startResponse = apiServer.start(); + await t.doesNotReject( + startResponse, + "failed to start API server with dynamic plugin imports configured for it...", + ); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoGrpc = (await startResponse).addressInfoGrpc; + const { address, port } = addressInfoGrpc; + const grpcHostAndPort = `${address}:${port}`; + + const apiClient = new default_service.org.hyperledger.cactus.cmd_api_server.DefaultServiceClient( + grpcHostAndPort, + grpc.credentials.createInsecure(), + ); + t.ok(apiClient, "apiClient truthy OK"); + + const responsePromise = new Promise< + health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB + >((resolve, reject) => { + apiClient.GetHealthCheckV1( + new empty.google.protobuf.Empty(), + ( + error: grpc.ServiceError | null, + response?: health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB, + ) => { + if (error) { + reject(error); + } else if (response) { + resolve(response); + } else { + throw new RuntimeError("No error, nor response received."); + } + }, + ); + }); + + await t.doesNotReject(responsePromise, "No error in healthcheck OK"); + const res = await responsePromise; + + const resHc = res?.toObject(); + + t.ok(resHc, `healthcheck response truthy OK`); + t.ok(resHc?.createdAt, `resHc.createdAt truthy OK`); + t.ok(resHc?.memoryUsage, `resHc.memoryUsage truthy OK`); + t.ok(resHc?.memoryUsage?.rss, `resHc.memoryUsage.rss truthy OK`); + t.ok(resHc?.success, `resHc.success truthy OK`); + t.end(); +}); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-m-tls-enabled.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-m-tls-enabled.test.ts new file mode 100644 index 0000000000..7183497ea9 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/grpc-proto-gen-ts-client-m-tls-enabled.test.ts @@ -0,0 +1,98 @@ +import test, { Test } from "tape-promise/tape"; + +import * as grpc from "@grpc/grpc-js"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { ApiServer, ConfigService } from "../../../main/typescript/public-api"; +import { SelfSignedPkiGenerator } from "../../../main/typescript/public-api"; +import { AuthorizationProtocol } from "../../../main/typescript/public-api"; +import { default_service } from "../../../main/typescript/public-api"; +import { health_check_response_pb } from "../../../main/typescript/public-api"; +import { empty } from "../../../main/typescript/public-api"; +import { RuntimeError } from "run-time-error"; + +const testCase = "API server: runs gRPC web services - mTLS"; +const logLevel: LogLevelDesc = "TRACE"; + +test(testCase, async (t: Test) => { + const generator = new SelfSignedPkiGenerator(); + t.ok(generator, "Instantiated SelfSignedCertificateGenerator OK."); + + const serverCert = generator.create("localhost"); + const clientCert = generator.create("client.localhost", serverCert); + const serverRootCertPemBuf = Buffer.from(serverCert.certificatePem); + + const configService = new ConfigService(); + const apiSrvOpts = configService.newExampleConfig(); + apiSrvOpts.authorizationProtocol = AuthorizationProtocol.NONE; + apiSrvOpts.configFile = ""; + apiSrvOpts.logLevel = logLevel; + apiSrvOpts.apiCorsDomainCsv = "*"; + apiSrvOpts.apiPort = 0; + apiSrvOpts.apiTlsCertPem = serverCert.certificatePem; + apiSrvOpts.apiTlsKeyPem = serverCert.privateKeyPem; + apiSrvOpts.apiTlsClientCaPem = clientCert.certificatePem; + apiSrvOpts.grpcPort = 0; + apiSrvOpts.cockpitPort = 0; + apiSrvOpts.grpcMtlsEnabled = true; + apiSrvOpts.apiTlsEnabled = false; + apiSrvOpts.plugins = []; + const config = configService.newExampleConfigConvict(apiSrvOpts); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + test.onFinish(async () => await apiServer.shutdown()); + + const startResponse = apiServer.start(); + await t.doesNotReject(startResponse, "API server started OK"); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoGrpc = (await startResponse).addressInfoGrpc; + const { address, port } = addressInfoGrpc; + const grpcHostAndPort = `${address}:${port}`; + + const tlsCredentials = grpc.credentials.createSsl( + serverRootCertPemBuf, + Buffer.from(clientCert.privateKeyPem), + Buffer.from(clientCert.certificatePem), + ); + const apiClient = new default_service.org.hyperledger.cactus.cmd_api_server.DefaultServiceClient( + grpcHostAndPort, + tlsCredentials, + ); + t.ok(apiClient, "apiClient truthy OK"); + + const responsePromise = new Promise< + health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB + >((resolve, reject) => { + apiClient.GetHealthCheckV1( + new empty.google.protobuf.Empty(), + ( + error: grpc.ServiceError | null, + response?: health_check_response_pb.org.hyperledger.cactus.cmd_api_server.HealthCheckResponsePB, + ) => { + if (error) { + reject(error); + } else if (response) { + resolve(response); + } else { + throw new RuntimeError("No error, nor response received."); + } + }, + ); + }); + + await t.doesNotReject(responsePromise, "No error in healthcheck OK"); + const res = await responsePromise; + + const resHc = res.toObject(); + + t.ok(resHc, `healthcheck response truthy OK`); + t.ok(resHc.createdAt, `resHc.createdAt truthy OK`); + t.ok(resHc.memoryUsage, `resHc.memoryUsage truthy OK`); + t.ok(resHc.memoryUsage?.rss, `resHc.memoryUsage.rss truthy OK`); + t.ok(resHc.success, `resHc.success truthy OK`); + t.end(); +}); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts index 94cdc17b5a..e446d691b9 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts @@ -59,6 +59,7 @@ test("can install plugin-consortium-manual", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ { diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts index 10faffb68e..9eae7e6a1c 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts @@ -39,6 +39,7 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ { diff --git a/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts b/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts index 97cecd31eb..9af45c9ddc 100644 --- a/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts +++ b/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts @@ -184,6 +184,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); @@ -226,6 +227,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo2.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts index d5fde64301..3c22eac030 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts @@ -47,6 +47,7 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ { diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts index d2cf5c9fb2..b596d9a683 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts @@ -73,6 +73,7 @@ test("NodeJS API server + Rust plugin work together", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = []; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts index 6879c9c0f0..7c6e075ec3 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts @@ -30,6 +30,7 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; apiServerOptions.plugins = [ { diff --git a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts index af919a71d7..e05254b4ba 100644 --- a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts +++ b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts @@ -161,6 +161,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); @@ -215,6 +216,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo2.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); @@ -270,6 +272,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo3.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-balance-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-balance-endpoint.test.ts index c5dd3da4a1..8255b4e650 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-balance-endpoint.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-balance-endpoint.test.ts @@ -105,6 +105,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts index 7ba80ad2a0..9559029338 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts @@ -103,6 +103,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-past-logs-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-past-logs-endpoint.test.ts index cd080ee85a..2e76012b3c 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-past-logs-endpoint.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-past-logs-endpoint.test.ts @@ -105,6 +105,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-transaction-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-transaction-endpoint.test.ts index 8e43cfb48b..fbc05a412a 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-transaction-endpoint.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-transaction-endpoint.test.ts @@ -105,6 +105,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/sign-transaction-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/sign-transaction-endpoint.test.ts index 266baff9fb..8fd57b4ff6 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/sign-transaction-endpoint.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/sign-transaction-endpoint.test.ts @@ -114,6 +114,7 @@ test(testCase, async (t: Test) => { apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = addressInfo1.port; apiServerOptions.cockpitPort = 0; + apiServerOptions.grpcPort = 0; apiServerOptions.apiTlsEnabled = false; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/yarn.lock b/yarn.lock index fee59cead6..ad7282c705 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2204,14 +2204,14 @@ dependencies: google-gax "^2.12.0" -"@grpc/grpc-js@^1.3.4", "@grpc/grpc-js@~1.3.0": - version "1.3.7" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.3.7.tgz#58b687aff93b743aafde237fd2ee9a3259d7f2d8" - integrity sha512-CKQVuwuSPh40tgOkR7c0ZisxYRiN05PcKPW72mQL5y++qd7CwBRoaJZvU5xfXnCJDFBmS3qZGQ71Frx6Ofo2XA== +"@grpc/grpc-js@1.3.6", "@grpc/grpc-js@^1.3.4", "@grpc/grpc-js@~1.3.0": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.3.6.tgz#6e2d17610c2c8df0f6ceab0e1968f563df74b173" + integrity sha512-v7+LQFbqZKmd/Tvf5/j1Xlbq6jXL/4d+gUtm2TNX4QiEC3ELWADmGr2dGlUyLl6aKTuYfsN72vAsO5zmavYkEg== dependencies: "@types/node" ">=12.12.47" -"@grpc/proto-loader@^0.6.1", "@grpc/proto-loader@^0.6.2": +"@grpc/proto-loader@0.6.4", "@grpc/proto-loader@^0.6.1", "@grpc/proto-loader@^0.6.2": version "0.6.4" resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.4.tgz#5438c0d771e92274e77e631babdc14456441cbdc" integrity sha512-7xvDvW/vJEcmLUltCUGOgWRPM8Oofv0eCFSVMuKqaqWJaXSzmB+m9hiyqe34QofAl4WAzIKUZZlinIF9FOHyTQ== @@ -2999,6 +2999,21 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@mapbox/node-pre-gyp@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" + integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + dependencies: + detect-libc "^1.0.3" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.1" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.4" + tar "^6.1.0" + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -3748,30 +3763,16 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/google-protobuf@3.15.3": + version "3.15.3" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.3.tgz#054fb37aecb34d7dec826e1ce2b40cc27ec3d06a" + integrity sha512-MDpu7lit927cdLtBzTPUFjXGANFUnu5ThPqjygY8XmCyI/oDlIA0jAi4sffGOxYaLK2CCxAuU9wGxsgAQbA6FQ== + "@types/http-cache-semantics@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/jasmine@*": - version "3.8.2" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.8.2.tgz#27ab0aaac29581bcbde5774e1843f90df977078e" - integrity sha512-u5h7dqzy2XpXTzhOzSNQUQpKGFvROF8ElNX9P/TJvsHnTg/JvsAseVsGWQAQQldqanYaM+5kwxW909BBFAUYsg== - -"@types/jasminewd2@2.0.10": - version "2.0.10" - resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.10.tgz#ae31c237aa6421bde30f1058b1d20f4577e54443" - integrity sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g== - dependencies: - "@types/jasmine" "*" - -"@types/jasminewd2@2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.3.tgz#0d2886b0cbdae4c0eeba55e30792f584bf040a95" - integrity sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg== - dependencies: - "@types/jasmine" "*" - "@types/joi@14.3.4": version "14.3.4" resolved "https://registry.yarnpkg.com/@types/joi/-/joi-14.3.4.tgz#eed1e14cbb07716079c814138831a520a725a1e0" @@ -10414,6 +10415,11 @@ google-p12-pem@^3.0.3: dependencies: node-forge "^0.10.0" +google-protobuf@3.15.8: + version "3.15.8" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.15.8.tgz#5f3948905e4951c867d6bc143f385a80e2a39efe" + integrity sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw== + got@9.6.0, got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -10500,6 +10506,21 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +grpc-tools@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/grpc-tools/-/grpc-tools-1.11.2.tgz#22d802d40012510ccc6591d11f9c94109ac07aab" + integrity sha512-4+EgpnnkJraamY++oyBCw5Hp9huRYfgakjNVKbiE3PgO9Tv5ydVlRo7ZyGJ0C0SEiA7HhbVc1sNNtIyK7FiEtg== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.5" + +grpc_tools_node_protoc_ts@5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.1.tgz#6f81ab7c8289c801cba3373aa334c13ca8f29618" + integrity sha512-OX6pWqN4BbjzdDdoJkkLoODO+XQnGC/hSHCDipF+ZtQlz3fLuYon+8rviUTuwE0etUZK9N34O4iucg3O7FFgyw== + dependencies: + google-protobuf "3.15.8" + handlebars "4.7.7" + gtoken@^5.0.4: version "5.3.1" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.1.tgz#c1c2598a826f2b5df7c6bb53d7be6cf6d50c3c78" @@ -10521,7 +10542,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.7.6: +handlebars@4.7.7, handlebars@^4.7.6: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -16745,6 +16766,11 @@ protobufjs@6.11.2, protobufjs@^6.10.0, protobufjs@^6.10.2, protobufjs@^6.11.2: "@types/node" ">=13.7.0" long "^4.0.0" +protoc-gen-ts@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/protoc-gen-ts/-/protoc-gen-ts-0.4.0.tgz#4be7e2086dc32db15f01733a7a200793f1e1081a" + integrity sha512-TUoAT45fC9Fpcbp+rOdjzUPytnYkV8YR0xQ9atZi69RNV2/if600cDxBPXXbujGIegTmiEaUkQIz4Rrk2lOyUQ== + protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"