diff --git a/.cspell.json b/.cspell.json index b45f60df1f..60adf2c909 100644 --- a/.cspell.json +++ b/.cspell.json @@ -32,6 +32,7 @@ "cccg", "cccs", "ccep", + "cccs", "ccid", "celo", "cids", @@ -62,6 +63,7 @@ "fidm", "flowdb", "fsouza", + "Fuzzer", "genproto", "GETHKEYCHAINPASSWORD", "ghcr", diff --git a/package.json b/package.json index aac14862c4..73347af798 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "tools:check-missing-node-deps": "TS_NODE_PROJECT=tools/tsconfig.json node --experimental-json-modules --trace-deprecation --experimental-modules --abort-on-uncaught-exception --loader ts-node/esm --experimental-specifier-resolution=node ./tools/custom-checks/check-missing-node-deps.ts", "generate-api-server-config": "node ./tools/generate-api-server-config.js", "sync-ts-config": "TS_NODE_PROJECT=tools/tsconfig.json node --experimental-json-modules --loader ts-node/esm ./tools/sync-npm-deps-to-tsc-projects.ts", - "start:api-server": "node ./packages/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js --config-file=.config.json", + "start:api-server": "node --max-http-header-size=4194304 ./packages/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js --config-file=.config.json", "start:example-supply-chain": "yarn build:dev && cd ./examples/cactus-example-supply-chain-backend/ && yarn && yarn 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", "start:example-cbdc-bridging-app": "node -r ts-node/register examples/cactus-example-cbdc-bridging-backend/dist/lib/main/typescript/cbdc-bridging-app-cli.js dotenv_config_path=examples/cactus-example-cbdc-bridging-backend/process.env", diff --git a/packages/cactus-cmd-api-server/src/main/json/openapi.json b/packages/cactus-cmd-api-server/src/main/json/openapi.json index 380d2110d0..e1d5d39102 100644 --- a/packages/cactus-cmd-api-server/src/main/json/openapi.json +++ b/packages/cactus-cmd-api-server/src/main/json/openapi.json @@ -154,6 +154,30 @@ } } } + }, + "/api/v1/api-server/get-aggregate-openapi-json": { + "get": { + "summary": "Returns the combined openapi.json document of all plugins currently installed in the API server.", + "description": "The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.", + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "get", + "path": "/api/v1/api-server/get-aggregate-openapi-json" + } + }, + "operationId": "getAggregateOpenapiJsonV1", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } } } } diff --git a/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/README.md b/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/README.md index 0deb13a613..2f8406ab88 100644 --- a/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/README.md +++ b/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/README.md @@ -44,6 +44,7 @@ All URIs are relative to *http://localhost* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- +*DefaultApi* | [**getAggregateOpenapiJsonV1**](docs/DefaultApi.md#getaggregateopenapijsonv1) | **GET** /api/v1/api-server/get-aggregate-openapi-json | Returns the combined openapi.json document of all plugins currently installed in the API server. *DefaultApi* | [**getHealthCheckV1**](docs/DefaultApi.md#gethealthcheckv1) | **GET** /api/v1/api-server/healthcheck | Can be used to verify liveness of an API server instance *DefaultApi* | [**getOpenApiSpecV1**](docs/DefaultApi.md#getopenapispecv1) | **GET** /api/v1/api-server/get-open-api-spec | *DefaultApi* | [**getPrometheusMetricsV1**](docs/DefaultApi.md#getprometheusmetricsv1) | **GET** /api/v1/api-server/get-prometheus-exporter-metrics | Get the Prometheus Metrics diff --git a/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/apis/DefaultApi.kt b/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/apis/DefaultApi.kt index d05dc1394b..a5e7e2b36b 100644 --- a/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/apis/DefaultApi.kt +++ b/packages/cactus-cmd-api-server/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/apis/DefaultApi.kt @@ -45,6 +45,74 @@ class DefaultApi(basePath: kotlin.String = defaultBasePath, client: OkHttpClient } } + /** + * Returns the combined openapi.json document of all plugins currently installed in the API server. + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @return kotlin.Any + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + * @throws UnsupportedOperationException If the API returns an informational or redirection response + * @throws ClientException If the API returns a client error response + * @throws ServerException If the API returns a server error response + */ + @Suppress("UNCHECKED_CAST") + @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) + fun getAggregateOpenapiJsonV1() : kotlin.Any { + val localVarResponse = getAggregateOpenapiJsonV1WithHttpInfo() + + return when (localVarResponse.responseType) { + ResponseType.Success -> (localVarResponse as Success<*>).data as kotlin.Any + ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.") + ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.") + ResponseType.ClientError -> { + val localVarError = localVarResponse as ClientError<*> + throw ClientException("Client error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) + } + ResponseType.ServerError -> { + val localVarError = localVarResponse as ServerError<*> + throw ServerException("Server error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) + } + } + } + + /** + * Returns the combined openapi.json document of all plugins currently installed in the API server. + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @return ApiResponse + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + */ + @Suppress("UNCHECKED_CAST") + @Throws(IllegalStateException::class, IOException::class) + fun getAggregateOpenapiJsonV1WithHttpInfo() : ApiResponse { + val localVariableConfig = getAggregateOpenapiJsonV1RequestConfig() + + return request( + localVariableConfig + ) + } + + /** + * To obtain the request config of the operation getAggregateOpenapiJsonV1 + * + * @return RequestConfig + */ + fun getAggregateOpenapiJsonV1RequestConfig() : RequestConfig { + val localVariableBody = null + val localVariableQuery: MultiValueMap = mutableMapOf() + val localVariableHeaders: MutableMap = mutableMapOf() + localVariableHeaders["Accept"] = "application/json" + + return RequestConfig( + method = RequestMethod.GET, + path = "/api/v1/api-server/get-aggregate-openapi-json", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + body = localVariableBody + ) + } + /** * Can be used to verify liveness of an API server instance * Returns the current timestamp of the API server as proof of health/liveness 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 index 17eade5cfe..4a2b806d37 100644 --- 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 @@ -13,9 +13,12 @@ syntax = "proto3"; package org.hyperledger.cactus.cmd_api_server; import "google/protobuf/empty.proto"; +import "models/any_type_pb.proto"; import "models/health_check_response_pb.proto"; service DefaultService { + rpc GetAggregateOpenapiJsonV1 (google.protobuf.Empty) returns (AnyTypePB); + rpc GetHealthCheckV1 (google.protobuf.Empty) returns (HealthCheckResponsePB); rpc GetOpenApiSpecV1 (google.protobuf.Empty) returns (GetOpenApiSpecV1Response); 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 b89bf3e88a..0bd27d866c 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 @@ -380,8 +380,8 @@ export class ApiServer { pluginImport: PluginImport, ): Promise { const fnTag = `ApiServer#installPluginPackage()`; - const pkgName = pluginImport.options.packageSrc - ? pluginImport.options.packageSrc + const pkgName = pluginImport.pluginPkgInstallSource + ? pluginImport.pluginPkgInstallSource : pluginImport.packageName; const instanceId = pluginImport.options.instanceId; diff --git a/packages/cactus-cmd-api-server/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-cmd-api-server/src/main/typescript/generated/openapi/typescript-axios/api.ts index 9791c88338..06b57f3e12 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -109,6 +109,36 @@ export type WatchHealthcheckV1 = typeof WatchHealthcheckV1[keyof typeof WatchHea */ export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @summary Returns the combined openapi.json document of all plugins currently installed in the API server. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAggregateOpenapiJsonV1: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/api-server/get-aggregate-openapi-json`; + // 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, + }; + }, /** * Returns the current timestamp of the API server as proof of health/liveness * @summary Can be used to verify liveness of an API server instance @@ -208,6 +238,16 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) return { + /** + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @summary Returns the combined openapi.json document of all plugins currently installed in the API server. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getAggregateOpenapiJsonV1(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getAggregateOpenapiJsonV1(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * Returns the current timestamp of the API server as proof of health/liveness * @summary Can be used to verify liveness of an API server instance @@ -247,6 +287,15 @@ export const DefaultApiFp = function(configuration?: Configuration) { export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = DefaultApiFp(configuration) return { + /** + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @summary Returns the combined openapi.json document of all plugins currently installed in the API server. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAggregateOpenapiJsonV1(options?: any): AxiosPromise { + return localVarFp.getAggregateOpenapiJsonV1(options).then((request) => request(axios, basePath)); + }, /** * Returns the current timestamp of the API server as proof of health/liveness * @summary Can be used to verify liveness of an API server instance @@ -283,6 +332,17 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa * @extends {BaseAPI} */ export class DefaultApi extends BaseAPI { + /** + * The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one. + * @summary Returns the combined openapi.json document of all plugins currently installed in the API server. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getAggregateOpenapiJsonV1(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getAggregateOpenapiJsonV1(options).then((request) => request(this.axios, this.basePath)); + } + /** * Returns the current timestamp of the API server as proof of health/liveness * @summary Can be used to verify liveness of an API server instance diff --git a/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/collect-openapi-json-docs.ts b/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/collect-openapi-json-docs.ts new file mode 100644 index 0000000000..2fa764b39c --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/collect-openapi-json-docs.ts @@ -0,0 +1,27 @@ +import type { OpenAPIV3 } from "express-openapi-validator/dist/framework/types"; + +import type { PluginRegistry } from "@hyperledger/cactus-core"; +import type { ICactusPlugin } from "@hyperledger/cactus-core-api"; +import type { IPluginWebService } from "@hyperledger/cactus-core-api"; +import { isIPluginWebService } from "@hyperledger/cactus-core-api"; +import { Checks } from "@hyperledger/cactus-common"; + +export async function collectOpenapiJsonDocs( + pr: PluginRegistry, +): Promise { + Checks.truthy(pr, `collectOpenapiJsonDocs() pr (PluginRegistry)`); + + const openApiJsonDocsPromises = pr + .getPlugins() + .filter((pluginInstance) => isIPluginWebService(pluginInstance)) + .map(async (plugin: ICactusPlugin) => { + const webSvc = plugin as IPluginWebService; + const openApiJson = (await webSvc.getOpenApiSpec()) as OpenAPIV3.Document; + return openApiJson; + }); + + const openApiJsonDocs = await Promise.all(openApiJsonDocsPromises); + + // Filter out falsy results where the plugin did not return anything. + return openApiJsonDocs.filter((d) => !!d); +} diff --git a/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/get-aggregate-openapi-json-v1-endpoint.ts b/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/get-aggregate-openapi-json-v1-endpoint.ts new file mode 100644 index 0000000000..5995807de0 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/main/typescript/web-services/get-aggregate-openapi-json/get-aggregate-openapi-json-v1-endpoint.ts @@ -0,0 +1,107 @@ +import { Express, Request, Response } from "express"; +import HttpStatus from "http-status-codes"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + PluginRegistry, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import OAS from "../../../json/openapi.json"; +import { collectOpenapiJsonDocs } from "./collect-openapi-json-docs"; + +export interface IGetAggregateOpenapiJsonEndpointV1Options { + logLevel?: LogLevelDesc; + pluginRegistry: PluginRegistry; +} + +export class GetAggregateOpenapiJsonEndpointV1 implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "GetAggregateOpenapiJsonEndpointV1"; + + private readonly log: Logger; + + public get className(): string { + return GetAggregateOpenapiJsonEndpointV1.CLASS_NAME; + } + + constructor(public readonly opts: IGetAggregateOpenapiJsonEndpointV1Options) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.pluginRegistry, `${fnTag} arg options.pluginRegistry`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public get oasPath(): typeof OAS.paths["/api/v1/api-server/get-aggregate-openapi-json"] { + return OAS.paths["/api/v1/api-server/get-aggregate-openapi-json"]; + } + + public getPath(): string { + return this.oasPath.get["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.get["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.get.operationId; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const verbUpper = this.getVerbLowerCase().toUpperCase(); + this.log.debug(`${verbUpper} ${this.getPath()}`); + + try { + const resBody = await collectOpenapiJsonDocs(this.opts.pluginRegistry); + res.status(HttpStatus.OK); + res.json(resBody); + } catch (ex) { + this.log.error(`${fnTag} failed to serve contract deploy request`, ex); + res.status(HttpStatus.INTERNAL_SERVER_ERROR); + res.statusMessage = ex.message; + res.json({ error: ex.stack }); + } + } + + public async getAggregateOpenapiJson(): Promise { + return {}; + } +} diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts index a01a884574..1e4577c0ae 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-from-github.test.ts @@ -40,12 +40,12 @@ test("can install plugins at runtime with specified version based on imports", a packageName: "@hyperledger/cactus-dummy-package", type: PluginImportType.Local, action: PluginImportAction.Install, + pluginPkgInstallSource: + "https://gitpkg.now.sh/hyperledger/cactus/packages/cactus-cmd-api-server/src/test/resources/cactus-dummy-package?main", options: { instanceId: uuidv4(), keychainId: uuidv4(), logLevel, - packageSrc: - "https://gitpkg.now.sh/hyperledger/cactus/packages/cactus-cmd-api-server/src/test/resources/cactus-dummy-package?main", }, }, ]; diff --git a/packages/cactus-core-api/src/main/json/openapi.json b/packages/cactus-core-api/src/main/json/openapi.json index dc1e45be8f..6239384d91 100644 --- a/packages/cactus-core-api/src/main/json/openapi.json +++ b/packages/cactus-core-api/src/main/json/openapi.json @@ -26,6 +26,13 @@ "maxLength": 1024, "nullable": false }, + "pluginPkgInstallSource": { + "type": "string", + "description": "If specified, it overrides the installation source of the package from the npm registry configured to be a folder on the local file-system. This is useful for testing how a plugin package behaves when loaded into the API server without having to publish it to a registry first.", + "minLength": 1, + "maxLength": 1024, + "nullable": false + }, "type": { "nullable": false, "description": "", diff --git a/packages/cactus-core-api/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/models/PluginImport.kt b/packages/cactus-core-api/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/models/PluginImport.kt index d246f35db2..a1dcc29d69 100644 --- a/packages/cactus-core-api/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/models/PluginImport.kt +++ b/packages/cactus-core-api/src/main/kotlin/generated/openapi/kotlin-client/src/main/kotlin/org/openapitools/client/models/PluginImport.kt @@ -27,6 +27,7 @@ import com.squareup.moshi.JsonClass * @param packageName * @param type * @param action + * @param pluginPkgInstallSource If specified, it overrides the installation source of the package from the npm registry configured to be a folder on the local file-system. This is useful for testing how a plugin package behaves when loaded into the API server without having to publish it to a registry first. * @param options */ @@ -42,6 +43,10 @@ data class PluginImport ( @Json(name = "action") val action: PluginImportAction, + /* If specified, it overrides the installation source of the package from the npm registry configured to be a folder on the local file-system. This is useful for testing how a plugin package behaves when loaded into the API server without having to publish it to a registry first. */ + @Json(name = "pluginPkgInstallSource") + val pluginPkgInstallSource: kotlin.String? = null, + @Json(name = "options") val options: kotlin.Any? = null diff --git a/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts index 34ce3b3146..312f3cdad9 100644 --- a/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -545,6 +545,12 @@ export interface PluginImport { * @memberof PluginImport */ 'packageName': string; + /** + * If specified, it overrides the installation source of the package from the npm registry configured to be a folder on the local file-system. This is useful for testing how a plugin package behaves when loaded into the API server without having to publish it to a registry first. + * @type {string} + * @memberof PluginImport + */ + 'pluginPkgInstallSource'?: string; /** * * @type {PluginImportType} diff --git a/packages/cactus-core/src/main/typescript/web-services/install-open-api-validator-middleware.ts b/packages/cactus-core/src/main/typescript/web-services/install-open-api-validator-middleware.ts index bc9a41da37..88e831c406 100644 --- a/packages/cactus-core/src/main/typescript/web-services/install-open-api-validator-middleware.ts +++ b/packages/cactus-core/src/main/typescript/web-services/install-open-api-validator-middleware.ts @@ -1,11 +1,13 @@ import type { Application, NextFunction, Request, Response } from "express"; import * as OpenApiValidator from "express-openapi-validator"; import { OpenAPIV3 } from "express-openapi-validator/dist/framework/types"; - +import type { ValidationError } from "express-openapi-validator/dist/framework/types"; import { error as EovErrors } from "express-openapi-validator"; import { Checks, + hasKey, + Logger, LoggerProvider, LogLevelDesc, } from "@hyperledger/cactus-common"; @@ -48,43 +50,92 @@ export async function installOpenapiValidationMiddleware( ignorePaths: (path: string) => !paths.includes(path), }), ); - app.use( - ( - err: { - status?: number; - errors: [ - { - path: string; - message: string; - errorCode: string; - }, - ]; - }, - req: Request, - res: Response, - next: NextFunction, - ) => { - const tag = "[express-openapi-validator-middleware-handler]"; - if (isOpenApiRequestValidationError(err)) { - if (err.status) { - const { errors, status } = err; - log.debug("%s Got valid error, status=%s - %o", tag, status, errors); - res.status(err.status); - res.send(err.errors); - } else { - log.debug("%s Got invalid error - status missing - %o", tag, err); - res.status(500); - res.send(err); - } - } else if (err) { - log.debug("%s Got invalid error - validator crash(?) - %o", tag, err); - res.status(500); - res.send(err); - } else { - log.debug("%s Validation Passed OK - %s", tag, req.url); - next(); - } - }, + + const log2 = LoggerProvider.getOrCreate({ + label: "expressOpenApiValidatorMiddlewareHandler()", + level: logLevel || "INFO", + }); + + app.use((ex: unknown, req: Request, res: Response, next: NextFunction) => + expressOpenApiValidatorMiddlewareHandler(log2, ex, req, res, next), + ); +} + +export function expressOpenApiValidatorMiddlewareHandler( + log: Logger, + ex: unknown, + req: Request, + res: Response, + next: NextFunction, +): void { + if (isOpenApiRequestValidationError(ex)) { + if (ex.status) { + const { errors, status } = ex; + const [{ message, path, errorCode }] = errors; + log.debug( + "Got valid error: %s path=%s, status=%s, message=%s, errorCode=%o", + req?.url, + path, + status, + message, + errorCode, + ); + res.status(ex.status); + res.send(ex.errors); + } else { + log.debug("Got invalid error: %s status missing - %o", req?.url, ex); + res.status(500); + res.json(ex); + } + } else if (isBodyParserError(ex)) { + const { type, statusCode, expose } = ex; + log.debug( + "BodyParser error: %s type=%s, statusCode=%o, expose=%o", + req?.url, + type, + statusCode, + expose, + ); + + res.status(ex.statusCode); + + if (ex.expose) { + res.json({ error: ex.message }); + } else { + res.json({ error: "ExpressJS req body parse failed (body-parser pkg)" }); + } + } else if (ex) { + log.debug("Got invalid error: %s unknown reason - %o", req?.url, ex); + res.status(500); + res.json(ex); + } else { + log.debug("%s Validation Passed OK - %s", req.url); + next(); + } +} + +export interface IBodyParserError { + readonly status: number; + readonly statusCode: number; + readonly expose: boolean; + readonly type: string; + readonly message: string; +} + +export function isBodyParserError(x: unknown): x is IBodyParserError { + return ( + hasKey(x, "status") && + typeof x.status === "number" && + isFinite(x.status) && + hasKey(x, "statusCode") && + typeof x.statusCode === "number" && + isFinite(x.statusCode) && + hasKey(x, "expose") && + typeof x.expose === "boolean" && + hasKey(x, "type") && + typeof x.type === "string" && + hasKey(x, "message") && + typeof x.message === "string" ); } @@ -109,7 +160,9 @@ export async function installOpenapiValidationMiddleware( * future, debugging these kind of errors are much easier and can be done based * on the logs alone (hopefully). */ -export function isOpenApiRequestValidationError(ex: unknown): boolean { +export function isOpenApiRequestValidationError( + ex: unknown, +): ex is ValidationError { if (ex) { return Object.values(EovErrors).some((x) => ex instanceof x); } else { diff --git a/packages/cactus-plugin-keychain-memory/src/main/typescript/web-services/get-keychain-entry-endpoint-v1.ts b/packages/cactus-plugin-keychain-memory/src/main/typescript/web-services/get-keychain-entry-endpoint-v1.ts index 87ed6b2ce8..51b8216b41 100644 --- a/packages/cactus-plugin-keychain-memory/src/main/typescript/web-services/get-keychain-entry-endpoint-v1.ts +++ b/packages/cactus-plugin-keychain-memory/src/main/typescript/web-services/get-keychain-entry-endpoint-v1.ts @@ -98,6 +98,19 @@ export class GetKeychainEntryV1Endpoint implements IWebServiceEndpoint { } catch (ex) { const errorMsg = `${reqTag} ${fnTag} Failed to deploy contract:`; await handleRestEndpointException({ errorMsg, log, error: ex, res }); + // FIXME - double check that the underlying implementation throws http errors + // if (ex?.message?.includes(`${key} secret not found`)) { + // res.status(404).json({ + // key, + // error: ex?.stack || ex?.message, + // }); + // } else { + // this.log.error(`Crash while serving ${reqTag}`, ex); + // res.status(500).json({ + // message: "Internal Server Error", + // error: ex?.stack || ex?.message, + // }); + // } } } } 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 73f3a57fcc..a143a67992 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 @@ -776,10 +776,6 @@ "type": "string", "nullable": false }, - "signingCredential": { - "$ref": "#/components/schemas/Web3SigningCredential", - "nullable": false - }, "invocationType": { "$ref": "#/components/schemas/EthContractInvocationType", "nullable": false, diff --git a/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/common/create-dummy-plugin-import-ledger-connector-besu.ts b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/common/create-dummy-plugin-import-ledger-connector-besu.ts new file mode 100644 index 0000000000..02abd21a8b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/common/create-dummy-plugin-import-ledger-connector-besu.ts @@ -0,0 +1,67 @@ +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { PluginImport } from "@hyperledger/cactus-core-api"; +import { createDummyPluginImport } from "@hyperledger/cactus-test-tooling"; +import { v4 as uuidv4 } from "uuid"; +import { IPluginLedgerConnectorBesuOptions } from "../../../main/typescript/public-api"; + +export async function createDummyOptionsLedgerConnectorBesu(req: { + readonly pluginRegistry: PluginRegistry; + readonly logLevel?: LogLevelDesc; +}): Promise { + const fnTag = "#createDummyOptionsLedgerConnectorBesu()" as const; + const { logLevel = "DEBUG", pluginRegistry } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: logLevel, + }); + + const options: IPluginLedgerConnectorBesuOptions = { + pluginRegistry, + instanceId: uuidv4(), + rpcApiHttpHost: "http://127.0.0.1:8000", + rpcApiWsHost: "ws://127.0.0.1:9000", + logLevel, + }; + + log.debug("created IPluginKeychainMemoryOptions=%o", options); + + return options; +} + +export async function createDummyPluginImportLedgerConnectorBesu(req: { + readonly pluginRegistry: PluginRegistry; + readonly gitRootDir: string; + readonly logLevel?: LogLevelDesc; + readonly overrides?: Partial; +}): Promise { + const pkgName = "@hyperledger/cactus-plugin-ledger-connector-besu" as const; + const pkgSubDir = "./packages/cactus-plugin-ledger-connector-besu/" as const; + const fnTag = "#createDummyPluginImportLedgerConnectorBesu()" as const; + const { logLevel = "DEBUG", pluginRegistry, gitRootDir, overrides } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: req.logLevel || "DEBUG", + }); + + const options = await createDummyOptionsLedgerConnectorBesu({ + pluginRegistry, + logLevel, + }); + + const pluginImport = + createDummyPluginImport({ + gitRootDir, + pkgName, + pkgSubDir, + logLevel, + overrides, + options, + }); + + log.debug("Created plugin import %s OK - %o", pkgName, pluginImport); + + return pluginImport; +} diff --git a/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/openapi-fuzzer-connector-besu.test.ts b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/openapi-fuzzer-connector-besu.test.ts new file mode 100644 index 0000000000..1e3abee718 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/unit/openapi-fuzzer-connector-besu.test.ts @@ -0,0 +1,69 @@ +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { OpenApiFuzzerContainer } from "@hyperledger/cactus-test-tooling"; +import "jest-extended"; +import OAS from "../../../main/json/openapi.json"; +import { createDummyPluginImportLedgerConnectorBesu } from "../common/create-dummy-plugin-import-ledger-connector-besu"; +import { PluginImport } from "@hyperledger/cactus-core-api"; +import path from "path"; +import { PluginRegistry } from "@hyperledger/cactus-core"; + +const PKG_NAME = "@hyperledger/cactus-plugin-ledger-connector-besu" as const; +const logLevel: LogLevelDesc = "DEBUG" as const; + +describe(PKG_NAME, async () => { + let fuzzer: OpenApiFuzzerContainer; + let apiUrl: string; + let pluginRegistry: PluginRegistry; + + const log = LoggerProvider.getOrCreate({ + label: __filename, + level: logLevel || "DEBUG", + }); + + beforeAll(async () => { + fuzzer = new OpenApiFuzzerContainer({ + emitContainerLogs: true, + logLevel, + }); + + const fuzzerStartRes = await fuzzer.start(); + expect(fuzzerStartRes).toBeTruthy(); + }); + + beforeAll(() => { + pluginRegistry = new PluginRegistry(); + }); + + beforeAll(async () => { + const pluginImports: PluginImport[] = []; + + const __dirname = path.dirname(__filename); + const SCRIPT_DIR = __dirname; + + log.debug(`SCRIPT_DIR: ${SCRIPT_DIR}`); + + const gitRootDir = path.join(SCRIPT_DIR, "../../../../../../"); + log.debug(`gitRootDir=${gitRootDir}`); + + const anImport = await createDummyPluginImportLedgerConnectorBesu({ + pluginRegistry, + logLevel, + gitRootDir, + }); + pluginImports.push(anImport); + + apiUrl = "FIXME TODO etc"; + }); + + afterAll(async () => { + await expect(fuzzer.stop()).toResolve(); + await expect(fuzzer.destroy()).toResolve(); + }); + + it("Passes the OpenAPI fuzzer tests", async () => { + await fuzzer.run({ + apiUrl, + spec: OAS, + }); + }); +}); diff --git a/packages/cactus-test-cmd-api-server/package.json b/packages/cactus-test-cmd-api-server/package.json index da89e554d3..7c8ad8410d 100644 --- a/packages/cactus-test-cmd-api-server/package.json +++ b/packages/cactus-test-cmd-api-server/package.json @@ -54,7 +54,31 @@ "@hyperledger/cactus-common": "2.0.0-alpha.2", "@hyperledger/cactus-core": "2.0.0-alpha.2", "@hyperledger/cactus-core-api": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-consortium-manual": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-htlc-eth-besu": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-htlc-eth-besu-erc20": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-keychain-aws-sm": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-keychain-azure-kv": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-keychain-google-sm": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-keychain-memory-wasm": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-keychain-vault": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-besu": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-cdl-socketio": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-corda": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-fabric": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-iroha": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-iroha2": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-quorum": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-sawtooth-socketio": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-ubiquity": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-xdai": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-odap-hermes": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-persistence-ethereum": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-persistence-fabric": "2.0.0-alpha.2", "express-jwt-authz": "2.4.1", "jose": "4.15.5", "uuid": "9.0.1" diff --git a/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-consortium-manual.ts b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-consortium-manual.ts new file mode 100644 index 0000000000..704ea3729e --- /dev/null +++ b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-consortium-manual.ts @@ -0,0 +1,83 @@ +import type { IPluginConsortiumManualOptions } from "@hyperledger/cactus-plugin-consortium-manual"; + +import { v4 as uuidv4 } from "uuid"; +import { generateKeyPair, exportPKCS8 } from "jose"; + +import { ConsortiumDatabase, PluginImport } from "@hyperledger/cactus-core-api"; +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { createDummyPluginImport } from "@hyperledger/cactus-test-tooling"; + +export async function createDummyOptionsConsortiumManual(req: { + readonly pluginRegistry: PluginRegistry; + readonly logLevel?: LogLevelDesc; +}): Promise { + const fnTag = "#createDummyOptionsConsortiumManual()" as const; + const { logLevel = "DEBUG", pluginRegistry } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: logLevel, + }); + + const keyPair = await generateKeyPair("ES256K"); + const keyPairPem = await exportPKCS8(keyPair.privateKey); + + // TODO: Make it configurable so that we can generate here a consortium of + // arbitrary size (orgs, nodes, etc.) + const consortiumDatabase: ConsortiumDatabase = { + cactusNode: [], + consortium: [], + consortiumMember: [], + ledger: [], + pluginInstance: [], + }; + + const options: IPluginConsortiumManualOptions = { + pluginRegistry, + instanceId: uuidv4(), + keyPairPem, + consortiumDatabase, + logLevel, + }; + + log.debug("created IPluginConsortiumManualOptions=%o", options); + + return options; +} + +export async function createDummyPluginImportConsortiumManual(req: { + readonly pluginRegistry: PluginRegistry; + readonly gitRootDir: string; + readonly logLevel?: LogLevelDesc; + readonly overrides?: Partial; +}): Promise { + const pkgName = "@hyperledger/cactus-plugin-consortium-manual" as const; + const pkgSubDir = "./packages/cactus-plugin-consortium-manual/" as const; + const fnTag = "#createDummyPluginImportConsortiumManual()" as const; + + const { logLevel = "DEBUG", pluginRegistry, gitRootDir, overrides } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: req.logLevel || "DEBUG", + }); + + const opts = await createDummyOptionsConsortiumManual({ + pluginRegistry, + logLevel, + }); + + const pluginImport = createDummyPluginImport({ + gitRootDir, + pkgName, + pkgSubDir, + logLevel, + overrides, + options: opts, + }); + + log.debug("Created plugin import %s OK - %o", pkgName, pluginImport); + + return pluginImport; +} diff --git a/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-keychain-memory.ts b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-keychain-memory.ts new file mode 100644 index 0000000000..8ac9b9b293 --- /dev/null +++ b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-import-keychain-memory.ts @@ -0,0 +1,64 @@ +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import type { IPluginKeychainMemoryOptions } from "@hyperledger/cactus-plugin-keychain-memory"; +import { v4 as uuidv4 } from "uuid"; +import { createDummyPluginImport } from "@hyperledger/cactus-test-tooling"; +import { PluginImport } from "@hyperledger/cactus-core-api"; + +export async function createDummyOptionsKeychainMemory(req: { + readonly pluginRegistry: PluginRegistry; + readonly logLevel?: LogLevelDesc; +}): Promise { + const fnTag = "#createDummyOptionsKeychainMemory()" as const; + const { logLevel = "DEBUG" } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: logLevel, + }); + + const options: IPluginKeychainMemoryOptions = { + instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel, + }; + + log.debug("created IPluginKeychainMemoryOptions=%o", options); + + return options; +} + +export async function createDummyPluginImportKeychainMemory(req: { + readonly pluginRegistry: PluginRegistry; + readonly gitRootDir: string; + readonly logLevel?: LogLevelDesc; + readonly overrides?: Partial; +}): Promise { + const pkgName = "@hyperledger/cactus-plugin-keychain-memory" as const; + const pkgSubDir = "./packages/cactus-plugin-keychain-memory/" as const; + const fnTag = "#createDummyPluginImportKeychainMemory()" as const; + const { logLevel = "DEBUG", pluginRegistry, gitRootDir, overrides } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: req.logLevel || "DEBUG", + }); + + const options = await createDummyOptionsKeychainMemory({ + pluginRegistry, + logLevel, + }); + + const pluginImport = createDummyPluginImport({ + gitRootDir, + pkgName, + pkgSubDir, + logLevel, + overrides, + options, + }); + + log.debug("Created plugin import %s OK - %o", pkgName, pluginImport); + + return pluginImport; +} diff --git a/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-imports.ts b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-imports.ts new file mode 100644 index 0000000000..375482f30f --- /dev/null +++ b/packages/cactus-test-cmd-api-server/src/main/typescript/dummy-plugin-imports/create-dummy-plugin-imports.ts @@ -0,0 +1,107 @@ +import type { IPluginHtlcEthBesuErc20Options } from "@hyperledger/cactus-plugin-htlc-eth-besu-erc20"; +import path from "path"; + +import { v4 as uuidv4 } from "uuid"; + +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { + PluginImport, + PluginImportAction, + PluginImportType, +} from "@hyperledger/cactus-core-api"; +import { createDummyPluginImportConsortiumManual } from "./create-dummy-plugin-import-consortium-manual"; +import { createDummyPluginImportKeychainMemory } from "./create-dummy-plugin-import-keychain-memory"; + +export async function createDummyPluginImports(req: { + readonly pluginRegistry: PluginRegistry; + readonly logLevel?: LogLevelDesc; +}): Promise { + const fnTag = "cactus-test-cmd-api-server#createDummyPluginImports()"; + const pluginImports: PluginImport[] = []; + const { logLevel = "DEBUG", pluginRegistry } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: req.logLevel || "DEBUG", + }); + + const __dirname = path.dirname(__filename); + const SCRIPT_DIR = __dirname; + + log.debug(`SCRIPT_DIR: ${SCRIPT_DIR}`); + + const gitRootDir = path.join(SCRIPT_DIR, "../../../../../../"); + log.debug(`gitRootDir=${gitRootDir}`); + + { + const anImport = await createDummyPluginImportKeychainMemory({ + pluginRegistry, + logLevel, + gitRootDir, + }); + pluginImports.push(anImport); + } + + { + const anImport = await createPluginImportHtlcEthBesuErc20({ + pluginRegistry, + logLevel, + gitRootDir, + }); + pluginImports.push(anImport); + } + + { + const anImport = await createDummyPluginImportConsortiumManual({ + pluginRegistry, + gitRootDir, + logLevel, + }); + pluginImports.push(anImport); + } + + return pluginImports; +} + +export async function createPluginImportHtlcEthBesuErc20(req: { + readonly pluginRegistry: PluginRegistry; + readonly gitRootDir: string; + readonly logLevel?: LogLevelDesc; + readonly overrides?: Partial; +}): Promise { + const packageName = "@hyperledger/cactus-plugin-htlc-eth-besu-erc20"; + const fnTag = "#createPluginImportHtlcEthBesuErc20()"; + const { logLevel = "DEBUG", pluginRegistry } = req; + + const pluginPkgInstallSource = path.join( + req.gitRootDir, + "./packages/cactus-plugin-htlc-eth-besu-erc20/", + ); + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: req.logLevel || "DEBUG", + }); + + log.debug( + "%s pluginPkgInstallSource=%s", + packageName, + pluginPkgInstallSource, + ); + + const options: IPluginHtlcEthBesuErc20Options = { + instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel, + pluginRegistry, + }; + + return { + packageName, + type: PluginImportType.Local, + action: PluginImportAction.Install, + pluginPkgInstallSource, + options, + }; +} diff --git a/packages/cactus-test-cmd-api-server/src/main/typescript/public-api.ts b/packages/cactus-test-cmd-api-server/src/main/typescript/public-api.ts index cb0ff5c3b5..02278c3844 100755 --- a/packages/cactus-test-cmd-api-server/src/main/typescript/public-api.ts +++ b/packages/cactus-test-cmd-api-server/src/main/typescript/public-api.ts @@ -1 +1,12 @@ -export {}; +export { createDummyPluginImport } from "@hyperledger/cactus-test-tooling"; +export { createDummyPluginImports } from "./dummy-plugin-imports/create-dummy-plugin-imports"; + +export { + createDummyOptionsConsortiumManual, + createDummyPluginImportConsortiumManual, +} from "./dummy-plugin-imports/create-dummy-plugin-import-consortium-manual"; + +export { + createDummyOptionsKeychainMemory, + createDummyPluginImportKeychainMemory, +} from "./dummy-plugin-imports/create-dummy-plugin-import-keychain-memory"; diff --git a/packages/cactus-test-tooling/package.json b/packages/cactus-test-tooling/package.json index 9ccd0e5566..041a1e2c9b 100644 --- a/packages/cactus-test-tooling/package.json +++ b/packages/cactus-test-tooling/package.json @@ -61,6 +61,7 @@ }, "dependencies": { "@hyperledger/cactus-common": "2.0.0-alpha.2", + "@hyperledger/cactus-core-api": "2.0.0-alpha.2", "axios": "1.6.0", "compare-versions": "3.6.0", "dockerode": "3.3.0", @@ -79,6 +80,7 @@ "node-ssh": "13.1.0", "p-retry": "4.6.1", "run-time-error-cjs": "1.4.0", + "safe-stable-stringify": "2.4.3", "socket.io": "4.5.4", "socket.io-client-fixed-types": "4.5.4", "tar-stream": "2.2.0", diff --git a/packages/cactus-test-tooling/src/main/typescript/common/create-dummy-plugin-import.ts b/packages/cactus-test-tooling/src/main/typescript/common/create-dummy-plugin-import.ts new file mode 100644 index 0000000000..9c03132a63 --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/common/create-dummy-plugin-import.ts @@ -0,0 +1,44 @@ +import path from "path"; + +import { PluginImport } from "@hyperledger/cactus-core-api"; +import { PluginImportAction } from "@hyperledger/cactus-core-api"; +import { PluginImportType } from "@hyperledger/cactus-core-api"; +import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; + +export function createDummyPluginImport(req: { + readonly gitRootDir: string; + readonly pkgName: string; + readonly pkgSubDir: string; + readonly options: T; + readonly logLevel?: LogLevelDesc; + readonly overrides?: Partial; +}): PluginImport { + const fnTag = "#createDummyPluginImport()"; + const { logLevel = "DEBUG", gitRootDir, pkgSubDir, options, pkgName } = req; + + const log = LoggerProvider.getOrCreate({ + label: fnTag, + level: logLevel || "DEBUG", + }); + + const pluginPkgInstallSource = path.join(gitRootDir, pkgSubDir); + + log.debug("%s pluginPkgInstallSource=%s", pkgName, pluginPkgInstallSource); + + const pluginImportDefaults: PluginImport = { + packageName: pkgName, + type: PluginImportType.Local, + action: PluginImportAction.Install, + pluginPkgInstallSource, + options, + }; + + log.debug("pluginImportDefaults=%o", pluginImportDefaults); + log.debug("req.overrides=%o", req.overrides); + + const pluginImportFinal = { ...pluginImportDefaults, ...req.overrides }; + + log.debug("pluginImportFinal=%o", pluginImportFinal); + + return pluginImportFinal; +} diff --git a/packages/cactus-test-tooling/src/main/typescript/openapi-fuzzer/openapi-fuzzer-container.ts b/packages/cactus-test-tooling/src/main/typescript/openapi-fuzzer/openapi-fuzzer-container.ts new file mode 100644 index 0000000000..2d2f34da6a --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/openapi-fuzzer/openapi-fuzzer-container.ts @@ -0,0 +1,300 @@ +import type { EventEmitter } from "events"; +import fs from "fs-extra"; +import { Optional } from "typescript-optional"; +import { RuntimeError } from "run-time-error-cjs"; +import type { Container } from "dockerode"; +import Docker from "dockerode"; +import { v4 as uuidV4 } from "uuid"; +import { stringify } from "safe-stable-stringify"; +import { Logger, Checks, Bools, newRex } from "@hyperledger/cactus-common"; +import type { LogLevelDesc } from "@hyperledger/cactus-common"; +import { LoggerProvider } from "@hyperledger/cactus-common"; +import { Containers } from "../common/containers"; +import path from "path"; + +export const K_DEFAULT_OPENAPI_FUZZER_CONTAINER_WORKDIR = + "/usr/src/host-sources-dir/"; + +export const K_DEFAULT_OPENAPI_FUZZER_IMAGE_NAME = + "ghcr.io/matusf/openapi-fuzzer" as const; + +export const K_DEFAULT_OPENAPI_FUZZER_IMAGE_VERSION = "v0.2.0" as const; + +export interface IOpenApiFuzzerContainerOptions { + readonly logLevel?: LogLevelDesc; + readonly imageName?: string; + readonly imageTag?: string; + /** + * The path on the host machine's file-system where the carbo build should + * occur. Note that host machine in this context means the machine that runs + * the test using this class, the one launching the containers. + */ + readonly hostSourceDir?: string; + readonly emitContainerLogs?: boolean; + readonly envVars?: Map; + readonly workDir?: string; +} + +/** + * Helper class designed to enable test cases running the OpenAPI Fuzzer tool + * without having to manage it's installation & dependencies. This class uses + * a container image under the hood to launch the tool (which was written in + * Rust) + * + * At it's core, it is meant to be a programamatic substitute for doing something + * like this on your local machine: + * + * ```sh + * $ docker run \ + * --user "$(id -u)":"$(id -g)" \ + * --volume "$PWD":/usr/src/myapp \ + * --workdir /usr/src/myapp \ + * --network=host \ + * ghcr.io/matusf/openapi-fuzzer:v0.2.0 \ + * run \ + * --spec /usr/src/myapp/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json \ + * --url http://localhost:4000/ \ + * --ignore-status-code 400 \ + * --ignore-status-code 404 + * ``` + * + * @see https://github.com/matusf/openapi-fuzzer + */ +export class OpenApiFuzzerContainer { + public static readonly CLASS_NAME = "OpenApiFuzzerContainer"; + + public readonly logLevel: LogLevelDesc; + public readonly imageName: string; + public readonly imageTag: string; + public readonly imageFqn: string; + public readonly log: Logger; + public readonly emitContainerLogs: boolean; + public readonly hostSourceDir: Optional; + public readonly workDir: string; + public readonly envVars: Map; + + private _containerId: Optional; + + public get containerId(): Optional { + return this._containerId; + } + + public get cwd(): string { + return this.workDir; + } + + public get container(): Optional { + const docker = new Docker(); + return this.containerId.isPresent() + ? Optional.ofNonNull(docker.getContainer(this.containerId.get())) + : Optional.empty(); + } + + public get className(): string { + return OpenApiFuzzerContainer.CLASS_NAME; + } + + constructor(public readonly opts: IOpenApiFuzzerContainerOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + + if (opts.hostSourceDir) { + Checks.nonBlankString(opts.hostSourceDir, `${fnTag} opts.hostSourceDir`); + } + + this._containerId = Optional.empty(); + this.hostSourceDir = Optional.ofNullable(opts.hostSourceDir); + this.imageName = opts.imageName || K_DEFAULT_OPENAPI_FUZZER_IMAGE_NAME; + this.imageTag = opts.imageTag || K_DEFAULT_OPENAPI_FUZZER_IMAGE_VERSION; + this.imageFqn = `${this.imageName}:${this.imageTag}`; + + this.envVars = opts.envVars || new Map(); + + this.emitContainerLogs = Bools.isBooleanStrict(opts.emitContainerLogs) + ? (opts.emitContainerLogs as boolean) + : true; + + this.workDir = opts.workDir + ? opts.workDir + : K_DEFAULT_OPENAPI_FUZZER_CONTAINER_WORKDIR; + + Checks.nonBlankString(this.workDir, `${fnTag} non-blank str this.workDir`); + + this.logLevel = opts.logLevel || "INFO"; + + const level = this.logLevel; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.log.debug(`Created instance of ${this.className} OK`); + } + + public async start(opts?: { + readonly omitPull: boolean; + }): Promise { + if (this.hostSourceDir.isPresent()) { + await this.checkHostSourceDirExists(); + } + + const docker = new Docker(); + if (this.containerId.isPresent()) { + this.log.warn(`Container ID provided. Will not start new one.`); + const container = docker.getContainer(this.containerId.get()); + return container; + } + if (opts!.omitPull !== true) { + this.log.debug(`Pulling image ${this.imageFqn}...`); + await Containers.pullImage(this.imageFqn, {}, this.opts.logLevel); + this.log.debug(`Pulled image ${this.imageFqn} OK`); + } + + const dockerEnvVars: string[] = new Array(...this.envVars).map( + (pairs) => `${pairs[0]}=${pairs[1]}`, + ); + + const Binds = this.hostSourceDir.isPresent() + ? [`${this.hostSourceDir.get()}:${this.cwd}`] + : []; + + const createOptions = { + WorkingDir: this.workDir, + Env: dockerEnvVars, + Healthcheck: { + Test: ["CMD-SHELL", `openapi-fuzzer --help run`], + Interval: 1000000000, // 1 second + Timeout: 3000000000, // 3 seconds + Retries: 10, + StartPeriod: 1000000000, // 1 second + }, + HostConfig: { + AutoRemove: true, + Binds, + }, + }; + + this.log.debug(`Starting ${this.imageFqn} with options: `, createOptions); + + return new Promise((resolve, reject) => { + const eventEmitter: EventEmitter = docker.run( + this.imageFqn, + [], + [], + createOptions, + {}, + (err: Error) => { + if (err) { + const errorMessage = `Failed to start container ${this.imageFqn}`; + const exception = new RuntimeError(errorMessage, err); + this.log.error(exception); + reject(exception); + } + }, + ); + + eventEmitter.once("start", async (container: Container) => { + const { id } = container; + this.log.debug(`Started ${this.imageFqn} successfully. ID=${id}`); + this._containerId = Optional.ofNonNull(id); + + if (this.emitContainerLogs) { + const fnTag = `[${this.imageFqn}]`; + await Containers.streamLogs({ + container: this.container.get(), + tag: fnTag, + log: this.log, + }); + } + + this.log.debug(`Registered container log stream callbacks OK`); + + try { + this.log.debug(`Starting to wait for healthcheck... `); + await Containers.waitForHealthCheck(this.containerId.get()); + this.log.debug(`Healthcheck passed OK`); + resolve(container); + } catch (ex: unknown) { + const eMsg = + `${this.className} Tried to wait for the ${this.imageFqn}` + + ` container healthcheck to pass but it failed. Check the inner` + + ` exception and the container logs for further information.`; + this.log.debug(eMsg, ex); + reject(newRex(eMsg, ex)); + } + }); + }); + } + + public async stop(): Promise { + return Containers.stop(this.container.get()); + } + + public async destroy(): Promise { + return this.container.get().remove(); + } + + public async checkHostSourceDirExists(): Promise { + const pathExists = await fs.pathExists(this.hostSourceDir.get()); + if (!pathExists) { + const errorMessage = `hostSourceDir ${this.hostSourceDir.get()} does not exist (or not accessible) on file-system. Cannot continue with Rust compilation`; + throw new RuntimeError(errorMessage); + } + } + + public async run(req: { + readonly spec: Record; + readonly apiUrl: string; + }): Promise<{ readonly results: Array }> { + const fnTag = `${this.className}#run()`; + if (!req) { + throw new RuntimeError(`${fnTag} arg "req" was falsy.`); + } + if (!req.apiUrl) { + throw new RuntimeError(`${fnTag} arg req.apiUrl was falsy.`); + } + if (typeof req.apiUrl !== "string") { + throw new RuntimeError(`${fnTag} arg req.apiUrl was non-string.`); + } + if (req.apiUrl.length <= 0) { + throw new RuntimeError(`${fnTag} arg req.apiUrl was blank-string.`); + } + if (!req.spec) { + throw new RuntimeError(`${fnTag} arg req.spec was falsy.`); + } + if (typeof req.spec !== "object") { + throw new RuntimeError(`${fnTag} arg req.spec was non-object type.`); + } + + const theContainer = this.container.get(); + + const dstFileName = uuidV4() + ".openapi.json"; + const srcFileAsString = stringify(req.spec, null, 2); + const dstFileDir = "/tmp/"; + const specAbsPathInContainer = path.join(dstFileDir, dstFileName); + + this.log.debug("specAbsPathInContainer=%s", specAbsPathInContainer); + + await Containers.putFile({ + containerOrId: theContainer, + dstFileDir, + dstFileName, + srcFileAsString, + }); + + const fuzzerCliRunOutput = await Containers.exec(theContainer, [ + "openapi-fuzzer", + "run", + "--spec", + specAbsPathInContainer, + "--url", + req.apiUrl, + "--ignore-status-code", + "400", + "--ignore-status-code", + "404", + ]); + + this.log.debug("fuzzerCliRunOutput=%s", fuzzerCliRunOutput); + + return { results: [] }; + } +} diff --git a/packages/cactus-test-tooling/src/main/typescript/public-api.ts b/packages/cactus-test-tooling/src/main/typescript/public-api.ts index ed5bff1d84..f5431fe486 100755 --- a/packages/cactus-test-tooling/src/main/typescript/public-api.ts +++ b/packages/cactus-test-tooling/src/main/typescript/public-api.ts @@ -217,3 +217,12 @@ export { FABRIC_25_LTS_FABRIC_SAMPLES__ORDERER_TLS_ROOTCERT_FILE_ORG_2, IFabricOrgEnvInfo, } from "./fabric/fabric-samples-env-constants"; +export { createDummyPluginImport } from "./common/create-dummy-plugin-import"; + +export { + IOpenApiFuzzerContainerOptions, + K_DEFAULT_OPENAPI_FUZZER_CONTAINER_WORKDIR, + K_DEFAULT_OPENAPI_FUZZER_IMAGE_NAME, + K_DEFAULT_OPENAPI_FUZZER_IMAGE_VERSION, + OpenApiFuzzerContainer, +} from "./openapi-fuzzer/openapi-fuzzer-container"; diff --git a/yarn.lock b/yarn.lock index 31b976c7b1..907e244968 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8307,7 +8307,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-keychain-aws-sm@workspace:packages/cactus-plugin-keychain-aws-sm": +"@hyperledger/cactus-plugin-keychain-aws-sm@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-keychain-aws-sm@workspace:packages/cactus-plugin-keychain-aws-sm": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-keychain-aws-sm@workspace:packages/cactus-plugin-keychain-aws-sm" dependencies: @@ -8332,7 +8332,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-keychain-azure-kv@workspace:packages/cactus-plugin-keychain-azure-kv": +"@hyperledger/cactus-plugin-keychain-azure-kv@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-keychain-azure-kv@workspace:packages/cactus-plugin-keychain-azure-kv": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-keychain-azure-kv@workspace:packages/cactus-plugin-keychain-azure-kv" dependencies: @@ -8356,7 +8356,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-keychain-google-sm@workspace:packages/cactus-plugin-keychain-google-sm": +"@hyperledger/cactus-plugin-keychain-google-sm@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-keychain-google-sm@workspace:packages/cactus-plugin-keychain-google-sm": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-keychain-google-sm@workspace:packages/cactus-plugin-keychain-google-sm" dependencies: @@ -8381,7 +8381,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-keychain-memory-wasm@workspace:packages/cactus-plugin-keychain-memory-wasm": +"@hyperledger/cactus-plugin-keychain-memory-wasm@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-keychain-memory-wasm@workspace:packages/cactus-plugin-keychain-memory-wasm": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-keychain-memory-wasm@workspace:packages/cactus-plugin-keychain-memory-wasm" dependencies: @@ -8512,6 +8512,26 @@ __metadata: languageName: unknown linkType: soft +"@hyperledger/cactus-plugin-ledger-connector-cdl-socketio@npm:2.0.0-alpha.2": + version: 2.0.0-alpha.2 + resolution: "@hyperledger/cactus-plugin-ledger-connector-cdl-socketio@npm:2.0.0-alpha.2" + dependencies: + axios: "npm:0.27.2" + body-parser: "npm:1.20.2" + config: "npm:3.3.7" + cookie-parser: "npm:1.4.6" + express: "npm:4.18.2" + fast-safe-stringify: "npm:2.1.1" + http-errors: "npm:1.6.3" + js-yaml: "npm:3.14.1" + jsonwebtoken: "npm:9.0.0" + log4js: "npm:6.4.1" + sanitize-html: "npm:2.7.0" + socket.io: "npm:4.5.4" + checksum: 10/7ae676797c5624a09599ce6a0497018017c296da810817914d797e27b9305ca0b73c9cf35f2fab4b190c39d7526da73ca243ead8b5814cf0b5f6170fed8015df + languageName: node + linkType: hard + "@hyperledger/cactus-plugin-ledger-connector-cdl@workspace:packages/cactus-plugin-ledger-connector-cdl": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-cdl@workspace:packages/cactus-plugin-ledger-connector-cdl" @@ -8660,7 +8680,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio@workspace:packages/cactus-plugin-ledger-connector-go-ethereum-socketio": +"@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio@workspace:packages/cactus-plugin-ledger-connector-go-ethereum-socketio": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio@workspace:packages/cactus-plugin-ledger-connector-go-ethereum-socketio" dependencies: @@ -8848,6 +8868,29 @@ __metadata: languageName: unknown linkType: soft +"@hyperledger/cactus-plugin-ledger-connector-sawtooth-socketio@npm:2.0.0-alpha.2": + version: 2.0.0-alpha.2 + resolution: "@hyperledger/cactus-plugin-ledger-connector-sawtooth-socketio@npm:2.0.0-alpha.2" + dependencies: + "@hyperledger/cactus-cmd-socketio-server": "npm:2.0.0-alpha.2" + "@types/node": "npm:14.18.54" + body-parser: "npm:1.17.2" + cbor: "npm:6.0.1" + cookie-parser: "npm:1.4.6" + debug: "npm:3.1.0" + express: "npm:4.17.3" + js-yaml: "npm:3.14.1" + jsonwebtoken: "npm:9.0.0" + log4js: "npm:6.4.1" + morgan: "npm:1.10.0" + serve-favicon: "npm:2.4.5" + shelljs: "npm:0.8.5" + socket.io: "npm:4.5.4" + xmlhttprequest: "npm:1.8.0" + checksum: 10/5089bd31f7e75951b5b856266b530337353921844ba69d9304f2e1caaed8039da374124559e04cbb31f26e4b565333c632c8bf5aacfcbe5fef9e2a4acb892a2f + languageName: node + linkType: hard + "@hyperledger/cactus-plugin-ledger-connector-sawtooth@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-sawtooth@workspace:packages/cactus-plugin-ledger-connector-sawtooth": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-sawtooth@workspace:packages/cactus-plugin-ledger-connector-sawtooth" @@ -8869,7 +8912,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@workspace:packages/cactus-plugin-ledger-connector-tcs-huawei-socketio": +"@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@workspace:packages/cactus-plugin-ledger-connector-tcs-huawei-socketio": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@workspace:packages/cactus-plugin-ledger-connector-tcs-huawei-socketio" dependencies: @@ -8896,7 +8939,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-ledger-connector-ubiquity@workspace:packages/cactus-plugin-ledger-connector-ubiquity": +"@hyperledger/cactus-plugin-ledger-connector-ubiquity@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-ubiquity@workspace:packages/cactus-plugin-ledger-connector-ubiquity": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-ubiquity@workspace:packages/cactus-plugin-ledger-connector-ubiquity" dependencies: @@ -8969,7 +9012,33 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-persistence-ethereum@workspace:packages/cactus-plugin-persistence-ethereum": +"@hyperledger/cactus-plugin-odap-hermes@npm:2.0.0-alpha.2": + version: 2.0.0-alpha.2 + resolution: "@hyperledger/cactus-plugin-odap-hermes@npm:2.0.0-alpha.2" + dependencies: + "@hyperledger/cactus-cmd-api-server": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-common": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-core": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-core-api": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-memory": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-besu": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-fabric": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-object-store-ipfs": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-test-tooling": "npm:2.0.0-alpha.2" + axios: "npm:0.21.4" + crypto-js: "npm:4.0.0" + knex: "npm:2.4.0" + secp256k1: "npm:4.0.3" + socket.io: "npm:4.5.4" + sqlite3: "npm:5.1.5" + typescript-optional: "npm:2.0.1" + web3: "npm:1.6.1" + web3-utils: "npm:1.6.1" + checksum: 10/0547432f1ead9e456ae3d189e12e1794ec24f5652e4d8d4f57584f2254e958fed0c87fcbb0494370d2223f76739ae171f80044875d6a29d89a44dc0241aa6bf6 + languageName: node + linkType: hard + +"@hyperledger/cactus-plugin-persistence-ethereum@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-persistence-ethereum@workspace:packages/cactus-plugin-persistence-ethereum": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-persistence-ethereum@workspace:packages/cactus-plugin-persistence-ethereum" dependencies: @@ -9002,7 +9071,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-plugin-persistence-fabric@workspace:packages/cactus-plugin-persistence-fabric": +"@hyperledger/cactus-plugin-persistence-fabric@npm:2.0.0-alpha.2, @hyperledger/cactus-plugin-persistence-fabric@workspace:packages/cactus-plugin-persistence-fabric": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-persistence-fabric@workspace:packages/cactus-plugin-persistence-fabric" dependencies: @@ -9093,7 +9162,31 @@ __metadata: "@hyperledger/cactus-common": "npm:2.0.0-alpha.2" "@hyperledger/cactus-core": "npm:2.0.0-alpha.2" "@hyperledger/cactus-core-api": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-consortium-manual": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-htlc-eth-besu": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-htlc-eth-besu-erc20": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-aws-sm": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-azure-kv": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-google-sm": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-memory": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-keychain-memory-wasm": "npm:2.0.0-alpha.2" "@hyperledger/cactus-plugin-keychain-vault": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-besu": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-cdl-socketio": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-corda": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-ethereum": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-fabric": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-iroha": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-iroha2": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-quorum": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-sawtooth-socketio": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-ubiquity": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-ledger-connector-xdai": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-odap-hermes": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-persistence-ethereum": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-plugin-persistence-fabric": "npm:2.0.0-alpha.2" "@hyperledger/cactus-test-tooling": "npm:2.0.0-alpha.2" "@types/uuid": "npm:9.0.8" express-jwt-authz: "npm:2.4.1" @@ -9264,6 +9357,7 @@ __metadata: "@aries-framework/node": "npm:0.5.0-alpha.58" "@hyperledger/aries-askar-nodejs": "npm:0.2.0-dev.1" "@hyperledger/cactus-common": "npm:2.0.0-alpha.2" + "@hyperledger/cactus-core-api": "npm:2.0.0-alpha.2" "@hyperledger/indy-vdr-nodejs": "npm:0.2.0-dev.3" "@types/dockerode": "npm:3.2.7" "@types/esm": "npm:3.2.0" @@ -9295,6 +9389,7 @@ __metadata: node-ssh: "npm:13.1.0" p-retry: "npm:4.6.1" run-time-error-cjs: "npm:1.4.0" + safe-stable-stringify: "npm:2.4.3" socket.io: "npm:4.5.4" socket.io-client-fixed-types: "npm:4.5.4" tar-stream: "npm:2.2.0" @@ -19782,6 +19877,24 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.17.2": + version: 1.17.2 + resolution: "body-parser@npm:1.17.2" + dependencies: + bytes: "npm:2.4.0" + content-type: "npm:~1.0.2" + debug: "npm:2.6.7" + depd: "npm:~1.1.0" + http-errors: "npm:~1.6.1" + iconv-lite: "npm:0.4.15" + on-finished: "npm:~2.3.0" + qs: "npm:6.4.0" + raw-body: "npm:~2.2.0" + type-is: "npm:~1.6.15" + checksum: 10/c879d89ba06b558e7e7ee66bced9ccb28ceb0ffa93c4536b756088b9204367c1f1f124d38702d51323070c1f299ca2f99a4e626d818a58520cba324b1d8cd641 + languageName: node + linkType: hard + "body-parser@npm:1.19.0": version: 1.19.0 resolution: "body-parser@npm:1.19.0" @@ -19800,6 +19913,24 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.19.2": + version: 1.19.2 + resolution: "body-parser@npm:1.19.2" + dependencies: + bytes: "npm:3.1.2" + content-type: "npm:~1.0.4" + debug: "npm:2.6.9" + depd: "npm:~1.1.2" + http-errors: "npm:1.8.1" + iconv-lite: "npm:0.4.24" + on-finished: "npm:~2.3.0" + qs: "npm:6.9.7" + raw-body: "npm:2.4.3" + type-is: "npm:~1.6.18" + checksum: 10/8a5f59d7e51b4000082a5e8bccf548cdbe77140520a55e380fecf5becf87a950b39e157a5c2a13334aa2ad45cc850b98e6c8652a4f033581c552001176dc9fc0 + languageName: node + linkType: hard + "body-parser@npm:1.20.1": version: 1.20.1 resolution: "body-parser@npm:1.20.1" @@ -20471,6 +20602,13 @@ __metadata: languageName: node linkType: hard +"bytes@npm:2.4.0": + version: 2.4.0 + resolution: "bytes@npm:2.4.0" + checksum: 10/2a3fd827e2e1299f687953384854b35ae2920553efc2062a6640c0dcf555af71f7a37a9d4d18fa0c35f2ac57ad9d0bfff526dbe8062a3996cc1ed30cc3f66392 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -22222,7 +22360,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:^1.0.5, content-type@npm:~1.0.5": +"content-type@npm:^1.0.4, content-type@npm:^1.0.5, content-type@npm:~1.0.2, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 @@ -22445,6 +22583,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:0.4.2, cookie@npm:^0.4.1, cookie@npm:~0.4.1": + version: 0.4.2 + resolution: "cookie@npm:0.4.2" + checksum: 10/2e1de9fdedca54881eab3c0477aeb067f281f3155d9cfee9d28dfb252210d09e85e9d175c0a60689661feb9e35e588515352f2456bc1f8e8db4267e05fd70137 + languageName: node + linkType: hard + "cookie@npm:0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" @@ -22452,13 +22597,6 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.4.1, cookie@npm:~0.4.1": - version: 0.4.2 - resolution: "cookie@npm:0.4.2" - checksum: 10/2e1de9fdedca54881eab3c0477aeb067f281f3155d9cfee9d28dfb252210d09e85e9d175c0a60689661feb9e35e588515352f2456bc1f8e8db4267e05fd70137 - languageName: node - linkType: hard - "cookiejar@npm:^2.1.1, cookiejar@npm:^2.1.2": version: 2.1.4 resolution: "cookiejar@npm:2.1.4" @@ -22931,6 +23069,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:4.0.0": + version: 4.0.0 + resolution: "crypto-js@npm:4.0.0" + checksum: 10/a67685014094c7704ce800e323e8e30a9200474a5574df07374e2a6f745a30923e5b668ef451c7f32676da40060252077b7773fd2267b693a901a6d0ab282fad + languageName: node + linkType: hard + "crypto-js@npm:4.2.0": version: 4.2.0 resolution: "crypto-js@npm:4.2.0" @@ -23536,6 +23681,15 @@ __metadata: languageName: node linkType: hard +"debug@npm:2.6.7": + version: 2.6.7 + resolution: "debug@npm:2.6.7" + dependencies: + ms: "npm:2.0.0" + checksum: 10/191c5a1f296dcafb9e936000032c0709501671ccb6677d504171dd9d1272860f8402eaa1092f4dfb52525189de10461963ebc511dedafc77ae6e41375672a6d5 + languageName: node + linkType: hard + "debug@npm:2.6.9, debug@npm:^2.1.1, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.0": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -24177,7 +24331,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:^1.1.0, depd@npm:^1.1.2, depd@npm:~1.1.2": +"depd@npm:^1.1.0, depd@npm:^1.1.2, depd@npm:~1.1.0, depd@npm:~1.1.2": version: 1.1.2 resolution: "depd@npm:1.1.2" checksum: 10/2ed6966fc14463a9e85451db330ab8ba041efed0b9a1a472dbfc6fbf2f82bab66491915f996b25d8517dddc36c8c74e24c30879b34877f3c4410733444a51d1d @@ -27881,6 +28035,44 @@ __metadata: languageName: node linkType: hard +"express@npm:4.17.3": + version: 4.17.3 + resolution: "express@npm:4.17.3" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:1.19.2" + content-disposition: "npm:0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:0.4.2" + cookie-signature: "npm:1.0.6" + debug: "npm:2.6.9" + depd: "npm:~1.1.2" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:~1.1.2" + fresh: "npm:0.5.2" + merge-descriptors: "npm:1.0.1" + methods: "npm:~1.1.2" + on-finished: "npm:~2.3.0" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:0.1.7" + proxy-addr: "npm:~2.0.7" + qs: "npm:6.9.7" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:0.17.2" + serve-static: "npm:1.14.2" + setprototypeof: "npm:1.2.0" + statuses: "npm:~1.5.0" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 10/e3970a6cccdeec918d249c9dd8b0a83bd64eb5730f33c69f449763d16155c3ff9631a3f5aa6191c7d56a6bb9d8b40fcce69c12563bf593208ec2b4902d7b8475 + languageName: node + linkType: hard + "express@npm:4.18.2, express@npm:^4.10.6, express@npm:^4.14.0, express@npm:^4.16.3, express@npm:^4.17.1, express@npm:^4.17.3": version: 4.18.2 resolution: "express@npm:4.18.2" @@ -31198,7 +31390,7 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:1.6.3, http-errors@npm:~1.6.2": +"http-errors@npm:1.6.3, http-errors@npm:~1.6.1, http-errors@npm:~1.6.2": version: 1.6.3 resolution: "http-errors@npm:1.6.3" dependencies: @@ -31223,6 +31415,19 @@ __metadata: languageName: node linkType: hard +"http-errors@npm:1.8.1": + version: 1.8.1 + resolution: "http-errors@npm:1.8.1" + dependencies: + depd: "npm:~1.1.2" + inherits: "npm:2.0.4" + setprototypeof: "npm:1.2.0" + statuses: "npm:>= 1.5.0 < 2" + toidentifier: "npm:1.0.1" + checksum: 10/76fc491bd8df2251e21978e080d5dae20d9736cfb29bb72b5b76ec1bcebb1c14f0f58a3a128dd89288934379d2173cfb0421c571d54103e93dd65ef6243d64d8 + languageName: node + linkType: hard + "http-errors@npm:2.0.0": version: 2.0.0 resolution: "http-errors@npm:2.0.0" @@ -31447,6 +31652,13 @@ __metadata: languageName: node linkType: hard +"iconv-lite@npm:0.4.15": + version: 0.4.15 + resolution: "iconv-lite@npm:0.4.15" + checksum: 10/738a0d1a4665dc02337e99ba6a0b72aa7b19d1c6e664554981a953c07ad1422cdf6ee0b139b05651f323a2293f7773ec84b86aa4395b157fa8892ac3666156f2 + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.17, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -43626,6 +43838,13 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.4.0": + version: 6.4.0 + resolution: "qs@npm:6.4.0" + checksum: 10/4e06bba119aff84a4f93e19ed61babdfb6165cff8c11f9d922120ba86b0c918854c53001c0ea689cc0b4107eb6cbb16f0acaffca22a6f5d7afd7795e31cd8dfd + languageName: node + linkType: hard + "qs@npm:6.7.0": version: 6.7.0 resolution: "qs@npm:6.7.0" @@ -43633,6 +43852,13 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.9.7": + version: 6.9.7 + resolution: "qs@npm:6.9.7" + checksum: 10/fb364b54bf4f092a095554968f5abf06036cfe359c9aba258a81b0c0366f625a46098fe1224b2a71ee2f88642470af391c7a8a1496508eca29c37093293f91a9 + languageName: node + linkType: hard + "qs@npm:^6.11.0, qs@npm:^6.9.4": version: 6.11.2 resolution: "qs@npm:6.11.2" @@ -43791,6 +44017,18 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:2.4.3": + version: 2.4.3 + resolution: "raw-body@npm:2.4.3" + dependencies: + bytes: "npm:3.1.2" + http-errors: "npm:1.8.1" + iconv-lite: "npm:0.4.24" + unpipe: "npm:1.0.0" + checksum: 10/b3a7cfacfa00778abce59fe1c698bd44edfdbea9ddd39e22c553dcd50a0376f4c3ad0e650f1ba9d20495bab81251844e14448292071487bd372e506faf0a1a2e + languageName: node + linkType: hard + "raw-body@npm:2.5.1": version: 2.5.1 resolution: "raw-body@npm:2.5.1" @@ -43827,6 +44065,17 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:~2.2.0": + version: 2.2.0 + resolution: "raw-body@npm:2.2.0" + dependencies: + bytes: "npm:2.4.0" + iconv-lite: "npm:0.4.15" + unpipe: "npm:1.0.0" + checksum: 10/763355cd670fdeaf6aa93c6b6ed13c661f8757571b7f86661254333319a4123cebaed82e6a63dc500a804e2ea12b89018b5bb13a420d35d6bfbacb9a2d31e6e3 + languageName: node + linkType: hard + "rc@npm:^1.2.7, rc@npm:^1.2.8": version: 1.2.8 resolution: "rc@npm:1.2.8" @@ -45947,6 +46196,20 @@ __metadata: languageName: node linkType: hard +"sanitize-html@npm:2.7.0": + version: 2.7.0 + resolution: "sanitize-html@npm:2.7.0" + dependencies: + deepmerge: "npm:^4.2.2" + escape-string-regexp: "npm:^4.0.0" + htmlparser2: "npm:^6.0.0" + is-plain-object: "npm:^5.0.0" + parse-srcset: "npm:^1.0.2" + postcss: "npm:^8.3.11" + checksum: 10/6ad6523e166fc681566fd893aee3ab0e611172467db51e120c6391e1216f1aa82e827444caf813b25e19c5fe237ebcde22da187c850b6cc19f22fc792aade807 + languageName: node + linkType: hard + "sanitize.css@npm:*": version: 13.0.0 resolution: "sanitize.css@npm:13.0.0" @@ -46285,6 +46548,27 @@ __metadata: languageName: node linkType: hard +"send@npm:0.17.2": + version: 0.17.2 + resolution: "send@npm:0.17.2" + dependencies: + debug: "npm:2.6.9" + depd: "npm:~1.1.2" + destroy: "npm:~1.0.4" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + fresh: "npm:0.5.2" + http-errors: "npm:1.8.1" + mime: "npm:1.6.0" + ms: "npm:2.1.3" + on-finished: "npm:~2.3.0" + range-parser: "npm:~1.2.1" + statuses: "npm:~1.5.0" + checksum: 10/b1e4f9b99a5571626dad1d4401363f6107e245c84e91ccae221ab55fea4d1f12be1b9ef657235381e2b9d3a5840b8ceb4d20c9c322e85535b587b12676bc340c + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -46433,6 +46717,18 @@ __metadata: languageName: node linkType: hard +"serve-static@npm:1.14.2": + version: 1.14.2 + resolution: "serve-static@npm:1.14.2" + dependencies: + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + parseurl: "npm:~1.3.3" + send: "npm:0.17.2" + checksum: 10/b84f9d58b07db2e4d6b1f60e60a27839f5247331b9bbfa1a90eda3523d8bc58585c914e069e785bb2645668e0b9cdd1a305bc1aa81c4284f5ead103741bb705c + languageName: node + linkType: hard + "serve-static@npm:1.15.0": version: 1.15.0 resolution: "serve-static@npm:1.15.0" @@ -50453,7 +50749,7 @@ __metadata: languageName: node linkType: hard -"type-is@npm:^1.6.18, type-is@npm:^1.6.4, type-is@npm:~1.6.17, type-is@npm:~1.6.18": +"type-is@npm:^1.6.18, type-is@npm:^1.6.4, type-is@npm:~1.6.15, type-is@npm:~1.6.17, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" dependencies: