diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json index 741d568064..ca58c96dbe 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json @@ -949,6 +949,31 @@ } }, "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-open-api-spec": { + "get": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "get", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-open-api-spec" + } + }, + "operationId": "getOpenApiSpecV1", + "summary": "Retrieves the .json file that contains the OpenAPI specification for the plugin.", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/deploy-contract-solidity-bytecode": { "post": { "x-hyperledger-cactus": { diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts index a5a63011f1..592098eee3 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -1423,6 +1423,36 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati options: localVarRequestOptions, }; }, + /** + * + * @summary Retrieves the .json file that contains the OpenAPI specification for the plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getOpenApiSpecV1: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-open-api-spec`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary Gets past logs, matching the given options. @@ -1679,6 +1709,16 @@ export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getBlockV1(getBlockV1Request, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary Retrieves the .json file that contains the OpenAPI specification for the plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getOpenApiSpecV1(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getOpenApiSpecV1(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @summary Gets past logs, matching the given options. @@ -1794,6 +1834,15 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa getBlockV1(getBlockV1Request?: GetBlockV1Request, options?: any): AxiosPromise { return localVarFp.getBlockV1(getBlockV1Request, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary Retrieves the .json file that contains the OpenAPI specification for the plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getOpenApiSpecV1(options?: any): AxiosPromise { + return localVarFp.getOpenApiSpecV1(options).then((request) => request(axios, basePath)); + }, /** * * @summary Gets past logs, matching the given options. @@ -1911,6 +1960,17 @@ export class DefaultApi extends BaseAPI { return DefaultApiFp(this.configuration).getBlockV1(getBlockV1Request, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary Retrieves the .json file that contains the OpenAPI specification for the plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getOpenApiSpecV1(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getOpenApiSpecV1(options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary Gets past logs, matching the given options. diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts index 4bf16ffa91..fc143f3622 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts @@ -95,6 +95,10 @@ import { RunTransactionEndpoint } from "./web-services/run-transaction-endpoint" import { GetBlockEndpoint } from "./web-services/get-block-v1-endpoint-"; import { GetBesuRecordEndpointV1 } from "./web-services/get-besu-record-endpoint-v1"; import { AbiItem } from "web3-utils"; +import { + GetOpenApiSpecV1Endpoint, + IGetOpenApiSpecV1EndpointOptions, +} from "./web-services/get-open-api-spec-v1-endpoint"; export const E_KEYCHAIN_NOT_FOUND = "cactus.connector.besu.keychain_not_found"; @@ -116,7 +120,8 @@ export class PluginLedgerConnectorBesu RunTransactionResponse >, ICactusPlugin, - IPluginWebService { + IPluginWebService +{ private readonly instanceId: string; public prometheusExporter: PrometheusExporter; private readonly log: Logger; @@ -288,6 +293,26 @@ export class PluginLedgerConnectorBesu const endpoint = new GetPrometheusExporterMetricsEndpointV1(opts); endpoints.push(endpoint); } + { + const oasPath = + OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-open-api-spec" + ]; + + const operationId = oasPath.get.operationId; + const opts: IGetOpenApiSpecV1EndpointOptions = { + oas: OAS, + oasPath, + operationId, + path: oasPath.get["x-hyperledger-cactus"].http.path, + pluginRegistry: this.pluginRegistry, + verbLowerCase: oasPath.get["x-hyperledger-cactus"].http.verbLowerCase, + logLevel: this.options.logLevel, + }; + const endpoint = new GetOpenApiSpecV1Endpoint(opts); + endpoints.push(endpoint); + } + this.endpoints = endpoints; return endpoints; } @@ -296,13 +321,12 @@ export class PluginLedgerConnectorBesu return `@hyperledger/cactus-plugin-ledger-connector-besu`; } - public async getConsensusAlgorithmFamily(): Promise< - ConsensusAlgorithmFamily - > { + public async getConsensusAlgorithmFamily(): Promise { return ConsensusAlgorithmFamily.Authority; } public async hasTransactionFinality(): Promise { - const currentConsensusAlgorithmFamily = await this.getConsensusAlgorithmFamily(); + const currentConsensusAlgorithmFamily = + await this.getConsensusAlgorithmFamily(); return consensusHasTransactionFinality(currentConsensusAlgorithmFamily); } @@ -451,18 +475,16 @@ export class PluginLedgerConnectorBesu req.signingCredential.type == Web3SigningCredentialType.CactusKeychainRef ) { - const { - keychainEntryKey, - keychainId, - } = req.signingCredential as Web3SigningCredentialCactusKeychainRef; + const { keychainEntryKey, keychainId } = + req.signingCredential as Web3SigningCredentialCactusKeychainRef; - const keychainPlugin = this.pluginRegistry.findOneByKeychainId( - keychainId, - ); + const keychainPlugin = + this.pluginRegistry.findOneByKeychainId(keychainId); privKey = await keychainPlugin?.get(keychainEntryKey); } else { - privKey = (req.signingCredential as Web3SigningCredentialPrivateKeyHex) - .secret; + privKey = ( + req.signingCredential as Web3SigningCredentialPrivateKeyHex + ).secret; } const fnParams = { @@ -476,9 +498,8 @@ export class PluginLedgerConnectorBesu throw new RuntimeError(`InvalidState: web3Quorum not initialized.`); } - const privacyGroupId = this.web3Quorum.utils.generatePrivacyGroup( - fnParams, - ); + const privacyGroupId = + this.web3Quorum.utils.generatePrivacyGroup(fnParams); this.log.debug("Generated privacyGroupId: ", privacyGroupId); callOutput = await this.web3Quorum.priv.call(privacyGroupId, { to: contractInstance.options.address, @@ -670,7 +691,7 @@ export class PluginLedgerConnectorBesu } return { - transactionReceipt: (txPoolReceipt as unknown) as Web3TransactionReceipt, + transactionReceipt: txPoolReceipt as unknown as Web3TransactionReceipt, }; } @@ -679,9 +700,8 @@ export class PluginLedgerConnectorBesu ): Promise { const fnTag = `${this.className}#transactPrivateKey()`; const { transactionConfig, web3SigningCredential } = req; - const { - secret, - } = web3SigningCredential as Web3SigningCredentialPrivateKeyHex; + const { secret } = + web3SigningCredential as Web3SigningCredentialPrivateKeyHex; // Run transaction to EEA client here if private transaction @@ -727,11 +747,8 @@ export class PluginLedgerConnectorBesu web3SigningCredential, privateTransactionConfig, } = req; - const { - ethAccount, - keychainEntryKey, - keychainId, - } = web3SigningCredential as Web3SigningCredentialCactusKeychainRef; + const { ethAccount, keychainEntryKey, keychainId } = + web3SigningCredential as Web3SigningCredentialCactusKeychainRef; // locate the keychain plugin that has access to the keychain backend // denoted by the keychainID from the request. diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-open-api-spec-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-open-api-spec-v1-endpoint.ts new file mode 100644 index 0000000000..b3029ae0aa --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-open-api-spec-v1-endpoint.ts @@ -0,0 +1,39 @@ +import { + GetOpenApiSpecV1EndpointBase, + IGetOpenApiSpecV1EndpointBaseOptions, +} from "@hyperledger/cactus-core"; + +import { Checks, LogLevelDesc } from "@hyperledger/cactus-common"; +import { IWebServiceEndpoint } from "@hyperledger/cactus-core-api"; + +import OAS from "../../json/openapi.json"; + +export const OasPathGetOpenApiSpecV1 = + OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-open-api-spec" + ]; + +export type OasPathTypeGetOpenApiSpecV1 = typeof OasPathGetOpenApiSpecV1; + +export interface IGetOpenApiSpecV1EndpointOptions + extends IGetOpenApiSpecV1EndpointBaseOptions< + typeof OAS, + OasPathTypeGetOpenApiSpecV1 + > { + readonly logLevel?: LogLevelDesc; +} + +export class GetOpenApiSpecV1Endpoint + extends GetOpenApiSpecV1EndpointBase + implements IWebServiceEndpoint +{ + public get className(): string { + return GetOpenApiSpecV1Endpoint.CLASS_NAME; + } + + constructor(public readonly options: IGetOpenApiSpecV1EndpointOptions) { + super(options); + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + } +} diff --git a/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/get-open-api-spec-v1-connector-besu.test.ts b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/get-open-api-spec-v1-connector-besu.test.ts new file mode 100644 index 0000000000..69a745a6d8 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/get-open-api-spec-v1-connector-besu.test.ts @@ -0,0 +1,89 @@ +import { + IListenOptions, + LogLevelDesc, + LoggerProvider, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { Constants, PluginImportType } from "@hyperledger/cactus-core-api"; +import bodyParser from "body-parser"; +import express from "express"; +import http from "http"; +import "jest-extended"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; +import { v4 as uuidv4 } from "uuid"; +import { + BesuApiClient, + BesuApiClientOptions, + PluginFactoryLedgerConnector, + PluginLedgerConnectorBesu, +} from "../../../main/typescript/public-api"; + +describe(__filename, () => { + const logLevel: LogLevelDesc = "TRACE"; + + const log = LoggerProvider.getOrCreate({ + label: __filename, + level: logLevel, + }); + + const rpcApiHttpHost = "http://127.0.0.1:8000"; + const rpcApiWsHost = "ws://127.0.0.1:9000"; + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + let apiClient: BesuApiClient; + + afterAll(async () => { + await Servers.shutdown(server); + }); + + beforeAll(async () => { + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + const connector: PluginLedgerConnectorBesu = await factory.create({ + rpcApiHttpHost, + rpcApiWsHost, + logLevel, + instanceId: uuidv4(), + pluginRegistry: new PluginRegistry({ plugins: [] }), + }); + + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + await connector.registerWebServices(expressApp, wsApi); + + const listenOptions: IListenOptions = { + hostname: "localhost", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; + + const besuApiClientOptions = new BesuApiClientOptions({ + basePath: apiHost, + }); + apiClient = new BesuApiClient(besuApiClientOptions); + log.debug("Instantiated BesuApiClient OK"); + }); + + it("Returns a JSON document containing the Open API specification of the plugin.", async () => { + const res1Promise = apiClient.getOpenApiSpecV1(); + await expect(res1Promise).resolves.not.toThrow(); + const res1 = await res1Promise; + expect(res1.status).toEqual(200); + expect(res1.data).toBeTruthy(); + expect(res1.config).toBeTruthy(); + expect(res1.config.url).toBeString(); + log.debug("Fetched URL OK=%s", res1.config.url); + expect(res1.data).toBeObject(); + }); +});