diff --git a/.cspell.json b/.cspell.json index 4cc20bb848..a8374d21e7 100644 --- a/.cspell.json +++ b/.cspell.json @@ -18,6 +18,7 @@ "caio", "cccs", "ccid", + "cctx", "cids", "Corda", "Cordapp", diff --git a/package.json b/package.json index 8c40fa725f..a1f35920ea 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,12 @@ "test:plugin-ledger-connector-quorum": "tap --ts --jobs=1 --timeout=60 \"packages/cactus-*-quorum/src/test/typescript/{unit,integration}/\"", "test:plugin-ledger-connector-iroha": "tap --ts --jobs=1 --timeout=600 \"packages/cactus-*-iroha/src/test/typescript/{unit,integration}/\"", "test:plugin-htlc-besu": "tap --jobs=1 --timeout=600 \"packages/*htlc-eth-besu/src/test/typescript/{integration}/\"", + "build:dev:plugin-consortium-manual": "lerna exec --stream --scope '*/*manual-consortium' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", + "build:dev:plugin-cc-tx-visualization": "lerna exec --stream --scope '*/*cc-tx-visualization' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", + "build:dev:example-supply-chain-backend": "lerna exec --stream --scope '*/*example-supply-chain-b*' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --display-modules --env=dev --target=node --config ../../webpack.config.js'", + "build:dev:example-carbon-accounting-backend": "lerna exec --stream --scope '*/*carbon-accounting-b*' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --display-modules --env=dev --target=node --config ../../webpack.config.js' && cp -r examples/cactus-example-carbon-accounting-backend/src/utility-emissions-channel/ examples/cactus-example-carbon-accounting-backend/dist/lib/", + "build:dev:sdk": "lerna exec --stream --scope '*/*sdk' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", + "build:dev:plugin-ledger-connector-corda": "lerna exec --stream --scope '*/*connector-corda' -- 'del-cli dist/** && npm run tsc && webpack --env=dev --target=node --config ../../webpack.config.js'", "test:plugin-ledger-connector-corda": "tap --ts --jobs=1 --timeout=600 \"packages/cactus-*-corda/src/test/typescript/{unit,integration}/\"", "webpack": "npm-run-all webpack:dev webpack:prod", "webpack:dev": "lerna run webpack:dev", diff --git a/packages/cactus-plugin-cc-tx-visualization/.gitignore b/packages/cactus-plugin-cc-tx-visualization/.gitignore new file mode 100644 index 0000000000..ce4bacc38a --- /dev/null +++ b/packages/cactus-plugin-cc-tx-visualization/.gitignore @@ -0,0 +1,2 @@ +cactus-openapi-spec-plugin-consortium-manual.json +src/main/typescript/generated/openapi/typescript-axios/.npmignore \ No newline at end of file diff --git a/packages/cactus-plugin-cc-tx-visualization/src/main/json/openapi.json b/packages/cactus-plugin-cc-tx-visualization/src/main/json/openapi.json new file mode 100644 index 0000000000..bb12f7401a --- /dev/null +++ b/packages/cactus-plugin-cc-tx-visualization/src/main/json/openapi.json @@ -0,0 +1,173 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Hyperledger Cactus Plugin - Consortium Web Service", + "description": "Manage a Cactus consortium through the APIs. Needs administrative privileges.", + "version": "0.0.1", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "https://www.cactus.stream/{basePath}", + "description": "Public test instance", + "variables": { + "basePath": { + "default": "" + } + } + }, + { + "url": "http://localhost:4000/{basePath}", + "description": "Local test instance", + "variables": { + "basePath": { + "default": "" + } + } + } + ], + "components": { + "schemas": { + "GetNodeJwsResponse": { + "type": "object", + "required": [ + "jws" + ], + "properties": { + "jws": { + "description": "The JSON Web Signature of the Cactus node.", + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/schemas/JWSGeneral", + "nullable": false + } + } + }, + "GetConsortiumJwsResponse": { + "type": "object", + "required": [ + "jws" + ], + "properties": { + "jws": { + "description": "The JSON Web Signature of the Cactus consortium.", + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/schemas/JWSGeneral", + "nullable": false, + "format": "The general format which is a JSON object, not a string." + } + } + }, + "PrometheusExporterMetricsResponse": { + "type": "string", + "nullable": false + }, + "GetNodeJwsRequest": { + "type": "object", + "properties": { + } + }, + "GetConsortiumJwsRequest": { + "type": "object", + "properties": { + } + } + } + }, + "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/consortium/jws": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/consortium/jws" + } + }, + "operationId": "getConsortiumJwsV1", + "summary": "Retrieves a consortium JWS", + "description": "The JWS asserting the consortium metadata (pub keys and hosts of nodes)", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetConsortiumJwsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetConsortiumJwsResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/node/jws": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/node/jws" + } + }, + "operationId": "getNodeJwsV1", + "summary": "Retrieves the JWT of a Cactus Node", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetNodeJwsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetNodeJwsResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/get-prometheus-exporter-metrics": { + "get": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "get", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-consortium-manual/get-prometheus-exporter-metrics" + } + }, + "operationId": "getPrometheusMetricsV1", + "summary": "Get the Prometheus Metrics", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/PrometheusExporterMetricsResponse" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.gitignore b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.gitignore new file mode 100644 index 0000000000..149b576547 --- /dev/null +++ b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore new file mode 100644 index 0000000000..6a6325b75c --- /dev/null +++ b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore @@ -0,0 +1,26 @@ +# 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 + +git_push.sh +.npmignore diff --git a/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/plugin-cc-tx-visualization.ts b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/plugin-cc-tx-visualization.ts index 9596730ee2..4aa830f99f 100644 --- a/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/plugin-cc-tx-visualization.ts +++ b/packages/cactus-plugin-cc-tx-visualization/src/main/typescript/plugin-cc-tx-visualization.ts @@ -30,7 +30,7 @@ export interface IWebAppOptions { export interface IPluginCcTxVisualizationOptions extends ICactusPluginOptions { prometheusExporter?: PrometheusExporter; - connectorRegistry?: PluginRegistry; + connectorRegistry: PluginRegistry; logLevel?: LogLevelDesc; webAppOptions?: IWebAppOptions; } @@ -65,9 +65,7 @@ export class PluginCcTxVisualization options.connectorRegistry, `${fnTag} options.connectorRegistry`, ); - if (options.connectorRegistry) - this.connectorRegistry = options.connectorRegistry; - else this.connectorRegistry = new PluginRegistry(); + this.connectorRegistry = options.connectorRegistry; // this.prometheusExporter.setNodeCount(this.getNodeCount()); } diff --git a/packages/cactus-plugin-cc-tx-visualization/src/test/typescript/unit/instantiation.test.ts b/packages/cactus-plugin-cc-tx-visualization/src/test/typescript/unit/instantiation.test.ts index 3a0170594e..2569a6b88a 100644 --- a/packages/cactus-plugin-cc-tx-visualization/src/test/typescript/unit/instantiation.test.ts +++ b/packages/cactus-plugin-cc-tx-visualization/src/test/typescript/unit/instantiation.test.ts @@ -1,13 +1,17 @@ +/* eslint-disable prettier/prettier */ +import { PluginRegistry } from "@hyperledger/cactus-core"; import test, { Test } from "tape"; import { v4 as uuidv4 } from "uuid"; import { IPluginCcTxVisualizationOptions, PluginCcTxVisualization, } from "../../../main/typescript/plugin-cc-tx-visualization"; +import { ICactusPlugin } from "@hyperledger/cactus-core-api"; -test("Instantiation", (t: Test) => { +test("Basic Instantiation", (t: Test) => { const options: IPluginCcTxVisualizationOptions = { instanceId: uuidv4(), + connectorRegistry: new PluginRegistry(), }; const pluginCcTxVisualization: PluginCcTxVisualization = new PluginCcTxVisualization( @@ -15,4 +19,42 @@ test("Instantiation", (t: Test) => { ); t.ok(pluginCcTxVisualization, "Instantiated"); + t.end(); +}); + +test("Dummy Connector Instantiaton", (t: Test) => { + class DummyPlugin implements ICactusPlugin{ + private readonly instanceId: string; + + constructor(){ + this.instanceId = "CCTX_DUMMY_" + uuidv4(); + } + public getInstanceId(): string { + return this.instanceId; + } + public getPackageName(): string { + return "DummyPlugin"; + } + public async onPluginInit(): Promise { + return; + } + + } + + + //add connector reference to the registry + const connectorRegistryTest = new PluginRegistry(); + connectorRegistryTest.add(new DummyPlugin()); + + const options: IPluginCcTxVisualizationOptions = { + instanceId: uuidv4(), + connectorRegistry: connectorRegistryTest, + }; + + const pluginCcTxVisualization: PluginCcTxVisualization = new PluginCcTxVisualization( + options, + ); + + t.ok(pluginCcTxVisualization, "Instantiated with a dummy connector"); + t.end(); }); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts index 8b314463c8..e01b4940ef 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import fs from "fs"; import path from "path"; import { Server } from "http"; @@ -105,6 +106,7 @@ import { CertDatastore, IIdentityData, } from "./identity/internal/cert-datastore"; +import {performance} from "perf_hooks"; /** * Constant value holding the default $GOPATH in the Fabric CLI container as * observed on fabric deployments that are produced by the official examples @@ -940,6 +942,10 @@ export class PluginLedgerConnectorFabric public async transact( req: RunTransactionRequest, ): Promise { + //start transaction time + const startTx = performance.now(); + + //start tx const fnTag = `${this.className}#transact()`; const { @@ -1043,6 +1049,12 @@ export class PluginLedgerConnectorFabric throw new Error(`${fnTag} unknown ${message}`); } } + //end of Tx + const endTx = performance.now(); + const txTimer = endTx - startTx; + this.prometheusExporter.addTimerOfCurrentTransaction(txTimer); + + //logging the transaction const outUtf8 = out.toString("utf-8"); const res: RunTransactionResponse = { functionOutput: outUtf8, @@ -1050,7 +1062,9 @@ export class PluginLedgerConnectorFabric }; gateway.disconnect(); this.log.debug(`transact() response: %o`, res); - this.prometheusExporter.addCurrentTransaction(); + + // wouldn't add this anymore because i can call it in the above method as i need the no of tx before the timer + //this.prometheusExporter.addCurrentTransaction(); return res; } catch (ex) { diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/metrics.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/metrics.ts index 0e4050177a..1668349509 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/metrics.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/metrics.ts @@ -2,8 +2,16 @@ import { Gauge } from "prom-client"; export const K_CACTUS_FABRIC_TOTAL_TX_COUNT = "cactus_fabric_total_tx_count"; +export const K_CACTUS_FABRIC_TX_LATENCY = "cactus_fabric_tx_latency"; + export const totalTxCount = new Gauge({ name: K_CACTUS_FABRIC_TOTAL_TX_COUNT, help: "Total transactions executed", labelNames: ["type"], }); + +export const txLatency = new Gauge({ + name: K_CACTUS_FABRIC_TX_LATENCY, + help: "Transaction latency", + labelNames: ["type"], +}); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/prometheus-exporter.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/prometheus-exporter.ts index 8939683942..fa119b0ee5 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/prometheus-exporter.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/prometheus-exporter.ts @@ -1,6 +1,11 @@ import promClient, { Registry } from "prom-client"; import { Transactions } from "./response.type"; -import { totalTxCount, K_CACTUS_FABRIC_TOTAL_TX_COUNT } from "./metrics"; +import { + totalTxCount, + K_CACTUS_FABRIC_TOTAL_TX_COUNT, + K_CACTUS_FABRIC_TX_LATENCY, + txLatency, +} from "./metrics"; export interface IPrometheusExporterOptions { pollingIntervalInMin?: number; @@ -8,7 +13,12 @@ export interface IPrometheusExporterOptions { export class PrometheusExporter { public readonly metricsPollingIntervalInMin: number; - public readonly transactions: Transactions = { counter: 0 }; + + public readonly transactions: Transactions = { + counter: 0, + totalTime: 0, + avgTime: 0, + }; public readonly registry: Registry; constructor( @@ -26,6 +36,14 @@ export class PrometheusExporter { .set(this.transactions.counter); } + public addTimerOfCurrentTransaction(txTimer: number): void { + this.addCurrentTransaction(); + this.transactions.totalTime = +txTimer; + this.transactions.avgTime = + this.transactions.totalTime / this.transactions.counter; + txLatency.labels(K_CACTUS_FABRIC_TX_LATENCY).set(this.transactions.avgTime); + } + public async getPrometheusMetrics(): Promise { const result = await this.registry.getSingleMetricAsString( K_CACTUS_FABRIC_TOTAL_TX_COUNT, diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/response.type.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/response.type.ts index 3f1bc7f491..c9cb6dcd1d 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/response.type.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/prometheus-exporter/response.type.ts @@ -1,3 +1,5 @@ export type Transactions = { counter: number; + totalTime: number; + avgTime: number; };