From 530389be8e604867e95e1961dccfed0b7d832743 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Thu, 14 May 2020 09:33:02 -0700 Subject: [PATCH] WIP - web service plugins #2 Signed-off-by: Peter Somogyvari --- .../src/main/typescript/api-server.ts | 224 +++++++++--------- .../src/main/typescript/cmd/bif-api.ts | 2 +- .../src/main/typescript/common/servers.ts | 29 +++ .../main/typescript/config/config-service.ts | 5 +- .../src/main/typescript/public-api.ts | 4 +- .../main/typescript/plugin/i-cactus-plugin.ts | 28 +++ .../i-plugin-ledger-connector.ts | 3 +- .../main/typescript/plugin/plugin-aspect.ts | 6 + .../storage/key-value/i-plugin-kv-storage.ts | 4 +- .../web-service/i-plugin-web-service.ts | 3 +- .../src/main/typescript/public-api.ts | 2 + .../main/typescript/plugin-keychain-memory.ts | 11 +- .../typescript/plugin-kv-storage-memory.ts | 11 +- .../plugin-ledger-connector-besu.ts | 10 +- .../plugin-ledger-connector-quorum.ts | 10 +- .../package.json | 1 + .../deploy-contract-via-web-service.ts | 50 +++- 17 files changed, 266 insertions(+), 137 deletions(-) create mode 100644 packages/bif-cmd-api-server/src/main/typescript/common/servers.ts create mode 100644 packages/bif-core-api/src/main/typescript/plugin/i-cactus-plugin.ts create mode 100644 packages/bif-core-api/src/main/typescript/plugin/plugin-aspect.ts diff --git a/packages/bif-cmd-api-server/src/main/typescript/api-server.ts b/packages/bif-cmd-api-server/src/main/typescript/api-server.ts index 8dbc59f5d6..dd3f20ea64 100644 --- a/packages/bif-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/bif-cmd-api-server/src/main/typescript/api-server.ts @@ -6,13 +6,15 @@ import { OpenApiValidator } from 'express-openapi-validator'; import compression from 'compression'; import bodyParser from 'body-parser'; import cors, { CorsOptions } from 'cors'; -import { IPluginKVStorage, PluginFactory } from '@hyperledger-labs/bif-core-api'; +import { IPluginKVStorage, PluginFactory, ICactusPlugin, PluginAspect } from '@hyperledger-labs/bif-core-api'; import { CreateConsortiumEndpointV1 } from './consortium/routes/create-consortium-endpoint-v1'; import { IBifApiServerOptions, ConfigService } from './config/config-service'; import { BIF_OPEN_API_JSON } from './openapi-spec'; import { Logger, LoggerProvider } from '@hyperledger-labs/bif-common'; +import { Servers } from './common/servers'; export interface IApiServerConstructorOptions { + plugins: ICactusPlugin[]; config: Config; } @@ -20,7 +22,7 @@ export class ApiServer { private readonly log: Logger; private httpServerApi: Server | null = null; - private httpServerFile: Server | null = null; + private httpServerCockpit: Server | null = null; constructor(public readonly options: IApiServerConstructorOptions) { if (!options) { @@ -43,141 +45,139 @@ export class ApiServer { } } - public shutdown(): Promise { - - const apiServerShutdown = new Promise((resolve, reject) => { - if (this.httpServerApi) { - this.httpServerApi.close((err: any) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - } - }); + public getHttpServerApi(): Server | null { + return this.httpServerApi; + } - const fileServerShutdown = new Promise((resolve, reject) => { - if (this.httpServerFile) { - this.httpServerFile.close((err: any) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - } - }); + public getHttpServerCockpit(): Server | null { + return this.httpServerCockpit; + } - return Promise.all([apiServerShutdown, fileServerShutdown]); + public async shutdown(): Promise { + + if (this.httpServerApi) { + this.log.info(`Closing HTTP server of the API...`); + await Servers.shutdown(this.httpServerApi); + this.log.info(`Close HTTP server of the API OK`); + } + + if (this.httpServerCockpit) { + this.log.info(`Closing HTTP server of the cockpit ...`); + await Servers.shutdown(this.httpServerCockpit); + this.log.info(`Close HTTP server of the cockpit OK`); + } } - async startCockpitFileServer(): Promise { - const cockpitWwwRoot = this.options.config.get('cockpitWwwRoot'); - this.log.info(`wwwRoot: ${cockpitWwwRoot}`); +async startCockpitFileServer(): Promise < void> { + const cockpitWwwRoot = this.options.config.get('cockpitWwwRoot'); + this.log.info(`wwwRoot: ${cockpitWwwRoot}`); - const resolvedWwwRoot = path.resolve(process.cwd(), cockpitWwwRoot); - this.log.info(`resolvedWwwRoot: ${resolvedWwwRoot}`); + const resolvedWwwRoot = path.resolve(process.cwd(), cockpitWwwRoot); + this.log.info(`resolvedWwwRoot: ${resolvedWwwRoot}`); - const resolvedIndexHtml = path.resolve(resolvedWwwRoot + '/index.html'); - this.log.info(`resolvedIndexHtml: ${resolvedIndexHtml}`); + const resolvedIndexHtml = path.resolve(resolvedWwwRoot + '/index.html'); + this.log.info(`resolvedIndexHtml: ${resolvedIndexHtml}`); - const app: Express = express(); - app.use(compression()); - app.use(express.static(resolvedWwwRoot)); - app.get('/*', (_, res) => res.sendFile(resolvedIndexHtml)); + const app: Express = express(); + app.use(compression()); + app.use(express.static(resolvedWwwRoot)); + app.get('/*', (_, res) => res.sendFile(resolvedIndexHtml)); - const cockpitPort: number = this.options.config.get('cockpitPort'); - const cockpitHost: string = this.options.config.get('cockpitHost'); + const cockpitPort: number = this.options.config.get('cockpitPort'); + const cockpitHost: string = this.options.config.get('cockpitHost'); - await new Promise((resolve, reject) => { - this.httpServerApi = app.listen(cockpitPort, cockpitHost, () => { - // tslint:disable-next-line: no-console - console.log(`BIF Cockpit UI reachable on port ${cockpitPort}`); - resolve({ cockpitPort }); - }); - this.httpServerApi.on('error', (err: any) => reject(err)); + await new Promise((resolve, reject) => { + this.httpServerCockpit = app.listen(cockpitPort, cockpitHost, () => { + this.log.info(`Cactus Cockpit UI reachable on port http://${cockpitHost}:${cockpitPort}`); + resolve({ cockpitPort }); }); - } + this.httpServerCockpit.on('error', (err: any) => reject(err)); + }); +} - async startApiServer(): Promise { - const app: Application = express(); - app.use(compression()); +async startApiServer(): Promise < void> { + const app: Application = express(); + app.use(compression()); - const corsMiddleware = this.createCorsMiddleware() + const corsMiddleware = this.createCorsMiddleware() app.use(corsMiddleware); - app.use(bodyParser.json({ limit: '50mb' })); + app.use(bodyParser.json({ limit: '50mb' })); - const openApiValidator = this.createOpenApiValidator(); - await openApiValidator.install(app); + const openApiValidator = this.createOpenApiValidator(); + await openApiValidator.install(app); - app.get('/healthcheck', (req: Request, res: Response, next: NextFunction) => { - res.json({ 'success': true, timestamp: new Date() }); - }); + app.get('/healthcheck', (req: Request, res: Response, next: NextFunction) => { + res.json({ 'success': true, timestamp: new Date() }); + }); - const storage: IPluginKVStorage = await this.createStoragePlugin(); - const configService = new ConfigService(); - const config = configService.getOrCreate(); + const storage: IPluginKVStorage = await this.createStoragePlugin(); + const configService = new ConfigService(); + const config = configService.getOrCreate(); { - const endpoint = new CreateConsortiumEndpointV1({ storage, config }); - app.post(endpoint.getPath(), endpoint.handleRequest.bind(endpoint)); - } + const endpoint = new CreateConsortiumEndpointV1({ storage, config }); + app.post(endpoint.getPath(), endpoint.handleRequest.bind(endpoint)); +} - // FIXME - // app.get('/api/v1/consortium/:consortiumId', (req: Request, res: Response, next: NextFunction) => { - // res.json({ swagger: 'TODO' }); - // }); - - const apiPort: number = this.options.config.get('apiPort'); - const apiHost: string = this.options.config.get('apiHost'); - this.log.info(`Binding Cactus API to port ${apiPort}...`); - await new Promise((resolve, reject) => { - const httpServer = app.listen(apiPort, apiHost, () => { - const address: any = httpServer.address(); - this.log.info(`Successfully bound API to port ${apiPort}`, { address }); - if (address && address.port) { - resolve({ port: address.port }); - } else { - resolve({ port: apiPort }); - } - }); - httpServer.on('error', (err) => reject(err)); - }); +// FIXME +// app.get('/api/v1/consortium/:consortiumId', (req: Request, res: Response, next: NextFunction) => { +// res.json({ swagger: 'TODO' }); +// }); + +const apiPort: number = this.options.config.get('apiPort'); +const apiHost: string = this.options.config.get('apiHost'); +this.log.info(`Binding Cactus API to port ${apiPort}...`); +await new Promise((resolve, reject) => { + const httpServerApi = app.listen(apiPort, apiHost, () => { + const address: any = httpServerApi.address(); + this.log.info(`Successfully bound API to port ${apiPort}`, { address }); + if (address && address.port) { + resolve({ port: address.port }); + } else { + resolve({ port: apiPort }); + } + }); + this.httpServerApi = httpServerApi; + this.httpServerApi.on('error', (err) => reject(err)); +}); } - createOpenApiValidator(): OpenApiValidator { - return new OpenApiValidator({ - apiSpec: BIF_OPEN_API_JSON, - validateRequests: true, - validateResponses: false - }); - } +createOpenApiValidator(): OpenApiValidator { + return new OpenApiValidator({ + apiSpec: BIF_OPEN_API_JSON, + validateRequests: true, + validateResponses: false + }); +} - async createStoragePlugin(): Promise { - const storagePluginPackage = this.options.config.get('storagePluginPackage'); - const { PluginFactoryKVStorage } = await import(storagePluginPackage); - const storagePluginOptionsJson = this.options.config.get('storagePluginOptionsJson'); - const storagePluginOptions = JSON.parse(storagePluginOptionsJson); - const pluginFactory: PluginFactory = new PluginFactoryKVStorage(); - const plugin = await pluginFactory.create(storagePluginOptions); - return plugin; +async createStoragePlugin(): Promise < IPluginKVStorage > { + const kvStoragePlugin = this.options.plugins.find((p) => p.getAspect() === PluginAspect.KV_STORAGE); + if(kvStoragePlugin) { + return kvStoragePlugin as IPluginKVStorage; } + const storagePluginPackage = this.options.config.get('storagePluginPackage'); + const { PluginFactoryKVStorage } = await import(storagePluginPackage); + const storagePluginOptionsJson = this.options.config.get('storagePluginOptionsJson'); + const storagePluginOptions = JSON.parse(storagePluginOptionsJson); + const pluginFactory: PluginFactory = new PluginFactoryKVStorage(); + const plugin = await pluginFactory.create(storagePluginOptions); + return plugin; +} - createCorsMiddleware(): RequestHandler { - const apiCorsDomainCsv = this.options.config.get('apiCorsDomainCsv'); - const allowedDomains = apiCorsDomainCsv.split(','); - const allDomainsAllowed = allowedDomains.includes('*'); - - const corsOptions: CorsOptions = { - origin: (origin: string | undefined, callback) => { - if (allDomainsAllowed || origin && allowedDomains.indexOf(origin) !== -1) { - callback(null, true); - } else { - callback(new Error(`CORS not allowed for Origin "${origin}".`)); - } +createCorsMiddleware(): RequestHandler { + const apiCorsDomainCsv = this.options.config.get('apiCorsDomainCsv'); + const allowedDomains = apiCorsDomainCsv.split(','); + const allDomainsAllowed = allowedDomains.includes('*'); + + const corsOptions: CorsOptions = { + origin: (origin: string | undefined, callback) => { + if (allDomainsAllowed || origin && allowedDomains.indexOf(origin) !== -1) { + callback(null, true); + } else { + callback(new Error(`CORS not allowed for Origin "${origin}".`)); } } - return cors(corsOptions); } + return cors(corsOptions); +} } diff --git a/packages/bif-cmd-api-server/src/main/typescript/cmd/bif-api.ts b/packages/bif-cmd-api-server/src/main/typescript/cmd/bif-api.ts index d4125639dc..b5d50b457d 100644 --- a/packages/bif-cmd-api-server/src/main/typescript/cmd/bif-api.ts +++ b/packages/bif-cmd-api-server/src/main/typescript/cmd/bif-api.ts @@ -17,7 +17,7 @@ const main = async () => { } else { const configService = new ConfigService(); const config = configService.getOrCreate(); - const apiServer = new ApiServer({ config }); + const apiServer = new ApiServer({ config, plugins: [] }); await apiServer.start(); } }; diff --git a/packages/bif-cmd-api-server/src/main/typescript/common/servers.ts b/packages/bif-cmd-api-server/src/main/typescript/common/servers.ts new file mode 100644 index 0000000000..d13d8a7025 --- /dev/null +++ b/packages/bif-cmd-api-server/src/main/typescript/common/servers.ts @@ -0,0 +1,29 @@ +import { Server } from 'http'; +import { Server as SecureServer } from 'https'; + +/** + * Utility class for handling common tasks for NodeJS HTTP/S server objects. + */ +export class Servers { + + /** + * Returns with a promise that resolves when the server has been shut down. Rejects if anything goes wrong of if the + * parameters are invalid. + * + * @param server The server object that will be shut down. + */ + public static async shutdown(server: Server | SecureServer): Promise { + if (!server) { + throw new TypeError(`Servers#shutdown() server was falsy. Need object.`); + } + return new Promise((resolve, reject) => { + server.close((err: any) => { + if (err) { + reject(new Error(`Servers#shutdown() Failed to shut down server: ${err.stack}`)); + } else { + resolve(); + } + }); + }); + } +} \ No newline at end of file diff --git a/packages/bif-cmd-api-server/src/main/typescript/config/config-service.ts b/packages/bif-cmd-api-server/src/main/typescript/config/config-service.ts index 700a769e84..58ba912dd2 100644 --- a/packages/bif-cmd-api-server/src/main/typescript/config/config-service.ts +++ b/packages/bif-cmd-api-server/src/main/typescript/config/config-service.ts @@ -224,8 +224,9 @@ export class ConfigService { }, {}); } - public newExampleConfigConvict(): Config { - const env = this.newExampleConfigEnv(); + public newExampleConfigConvict(bifApiServerOptions?: IBifApiServerOptions): Config { + bifApiServerOptions = bifApiServerOptions || this.newExampleConfig(); + const env = this.newExampleConfigEnv(bifApiServerOptions); return this.getOrCreate({ env }); } diff --git a/packages/bif-cmd-api-server/src/main/typescript/public-api.ts b/packages/bif-cmd-api-server/src/main/typescript/public-api.ts index afb9cbfa0a..af4460d8f8 100755 --- a/packages/bif-cmd-api-server/src/main/typescript/public-api.ts +++ b/packages/bif-cmd-api-server/src/main/typescript/public-api.ts @@ -1,3 +1,3 @@ -export { ApiServer } from './api-server'; -export { ConfigService } from './config/config-service'; +export { ApiServer, IApiServerConstructorOptions } from './api-server'; +export { ConfigService, IBifApiServerOptions } from './config/config-service'; export { CreateConsortiumEndpointV1, ICreateConsortiumEndpointOptions } from './consortium/routes/create-consortium-endpoint-v1'; diff --git a/packages/bif-core-api/src/main/typescript/plugin/i-cactus-plugin.ts b/packages/bif-core-api/src/main/typescript/plugin/i-cactus-plugin.ts new file mode 100644 index 0000000000..1003ec35ba --- /dev/null +++ b/packages/bif-core-api/src/main/typescript/plugin/i-cactus-plugin.ts @@ -0,0 +1,28 @@ +import { PluginAspect } from "./plugin-aspect"; + +/** + * This is the common base for all other plugin interface definitions to have as a parent. + * + */ +export interface ICactusPlugin { + + /** + * Returns the ID of the plugin which is a string uniquely identifying the plugin among other plugins so that they can + * be managed separately without conflicts or runtime errors. + * Important: This is not just uniqely identifying the plugin aspect, but the implementation as well. + * For example a plugin aspect would we `ledger-connector` or `storage` and implementations are the ones within those + * aspects such as `plugin-ledger-connector-besu` or `plugin-storage-kv-in-memory`. + */ + getId(): string; + + /** + * Returns the aspect of which this plugin implementation belongs to such as the aspect of `ledger-connector` or + * `storage` for example. + * There can be any number of plugin implementations for each aspect. + */ + getAspect(): PluginAspect; +} + +export function isICactusPlugin(pluginInstance: ICactusPlugin): pluginInstance is ICactusPlugin { + return typeof pluginInstance.getId === 'function'; +} diff --git a/packages/bif-core-api/src/main/typescript/plugin/ledger-connector/i-plugin-ledger-connector.ts b/packages/bif-core-api/src/main/typescript/plugin/ledger-connector/i-plugin-ledger-connector.ts index 28299078e5..fc27ccf2ca 100644 --- a/packages/bif-core-api/src/main/typescript/plugin/ledger-connector/i-plugin-ledger-connector.ts +++ b/packages/bif-core-api/src/main/typescript/plugin/ledger-connector/i-plugin-ledger-connector.ts @@ -1,8 +1,9 @@ +import { ICactusPlugin } from "../i-cactus-plugin"; /** * Common interface to be implemented by plugins which are implementing the connection to ledgers. */ -export interface IPluginLedgerConnector { +export interface IPluginLedgerConnector extends ICactusPlugin { /** * Deploys the BIF build-in smart contract written for this ledger to manage the validator's public keys. diff --git a/packages/bif-core-api/src/main/typescript/plugin/plugin-aspect.ts b/packages/bif-core-api/src/main/typescript/plugin/plugin-aspect.ts new file mode 100644 index 0000000000..37d3cf6b4c --- /dev/null +++ b/packages/bif-core-api/src/main/typescript/plugin/plugin-aspect.ts @@ -0,0 +1,6 @@ +export const enum PluginAspect { + KEYCHAIN = 'KEYCHAIN', + LEDGER_CONNECTOR = 'LEDGER_CONNECTOR', + KV_STORAGE = 'KV_STORAGE', + WEB_SERVICE = 'WEB_SERVICE', +} \ No newline at end of file diff --git a/packages/bif-core-api/src/main/typescript/plugin/storage/key-value/i-plugin-kv-storage.ts b/packages/bif-core-api/src/main/typescript/plugin/storage/key-value/i-plugin-kv-storage.ts index 4e36645445..d06c71c94f 100644 --- a/packages/bif-core-api/src/main/typescript/plugin/storage/key-value/i-plugin-kv-storage.ts +++ b/packages/bif-core-api/src/main/typescript/plugin/storage/key-value/i-plugin-kv-storage.ts @@ -1,4 +1,6 @@ -export interface IPluginKVStorage { +import { ICactusPlugin } from "../../i-cactus-plugin"; + +export interface IPluginKVStorage extends ICactusPlugin { has(key: string): Promise; get(key: string): Promise; set(key: string, value: T): Promise; diff --git a/packages/bif-core-api/src/main/typescript/plugin/web-service/i-plugin-web-service.ts b/packages/bif-core-api/src/main/typescript/plugin/web-service/i-plugin-web-service.ts index 14aaefdeee..5a3bd94665 100644 --- a/packages/bif-core-api/src/main/typescript/plugin/web-service/i-plugin-web-service.ts +++ b/packages/bif-core-api/src/main/typescript/plugin/web-service/i-plugin-web-service.ts @@ -1,6 +1,7 @@ import { IWebServiceEndpoint } from "./i-web-service-endpoint"; +import { ICactusPlugin } from "../i-cactus-plugin"; -export interface IPluginWebService { +export interface IPluginWebService extends ICactusPlugin { installWebService(expressApp: any): IWebServiceEndpoint[]; } diff --git a/packages/bif-core-api/src/main/typescript/public-api.ts b/packages/bif-core-api/src/main/typescript/public-api.ts index a872435053..8db6da2437 100755 --- a/packages/bif-core-api/src/main/typescript/public-api.ts +++ b/packages/bif-core-api/src/main/typescript/public-api.ts @@ -5,3 +5,5 @@ export { IExpressRequestHandler } from './plugin/web-service/i-express-request-h export { IPluginWebService, isIPluginWebService } from './plugin/web-service/i-plugin-web-service'; export { IWebServiceEndpoint } from './plugin/web-service/i-web-service-endpoint'; export { PluginFactory } from './plugin/plugin-factory'; +export { ICactusPlugin, isICactusPlugin } from './plugin/i-cactus-plugin'; +export { PluginAspect } from './plugin/plugin-aspect'; diff --git a/packages/bif-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts b/packages/bif-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts index f3d41d8491..72b8f35b81 100644 --- a/packages/bif-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts +++ b/packages/bif-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts @@ -1,10 +1,11 @@ -import { IPluginKeychain } from '@hyperledger-labs/bif-core-api'; +import { IPluginKeychain, PluginAspect } from '@hyperledger-labs/bif-core-api'; export interface IPluginKeychainOptions { backend: Map; } export class PluginKeychainMemory implements IPluginKeychain { + constructor(public readonly options: IPluginKeychainOptions) { if (!options) { throw new Error(`PluginKeychainMemory#ctor options falsy.`); @@ -14,6 +15,14 @@ export class PluginKeychainMemory implements IPluginKeychain { } } + public getId(): string { + return `@hyperledger/cactus-plugin-keychain-memory`; + } + + public getAspect(): PluginAspect { + return PluginAspect.KEYCHAIN; + } + async rotateEncryptionKeys(): Promise { throw new Error("Method not implemented."); } diff --git a/packages/bif-plugin-kv-storage-memory/src/main/typescript/plugin-kv-storage-memory.ts b/packages/bif-plugin-kv-storage-memory/src/main/typescript/plugin-kv-storage-memory.ts index f075b6cee9..4963112e04 100644 --- a/packages/bif-plugin-kv-storage-memory/src/main/typescript/plugin-kv-storage-memory.ts +++ b/packages/bif-plugin-kv-storage-memory/src/main/typescript/plugin-kv-storage-memory.ts @@ -1,10 +1,11 @@ -import { IPluginKVStorage } from '@hyperledger-labs/bif-core-api'; +import { IPluginKVStorage, PluginAspect } from '@hyperledger-labs/bif-core-api'; export interface IPluginKVStorageOptions { backend: Map; } export class PluginKVStorageMemory implements IPluginKVStorage { + constructor(public readonly options: IPluginKVStorageOptions) { if (!options) { throw new Error(`PluginKVStorageMemory#ctor options falsy.`); @@ -14,6 +15,14 @@ export class PluginKVStorageMemory implements IPluginKVStorage { } } + public getId(): string { + return `@hyperledger/cactus-plugin-kv-storage-memory`; + } + + public getAspect(): PluginAspect { + return PluginAspect.KV_STORAGE; + } + async get(key: string): Promise { return this.options.backend.get(key); } diff --git a/packages/bif-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts b/packages/bif-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts index 8c503bac29..9ce34bfd32 100644 --- a/packages/bif-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts +++ b/packages/bif-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts @@ -1,4 +1,4 @@ -import { IPluginLedgerConnector } from '@hyperledger-labs/bif-core-api'; +import { IPluginLedgerConnector, PluginAspect } from '@hyperledger-labs/bif-core-api'; import Web3 from 'web3'; import EEAClient, { IWeb3InstanceExtended } from 'web3-eea'; @@ -24,6 +24,14 @@ export class PluginLedgerConnectorBesu implements IPluginLedgerConnector { return this.web3Eea.eea.sendRawTransaction(options); } diff --git a/packages/bif-plugin-ledger-connector-quorum/src/main/typescript/plugin-ledger-connector-quorum.ts b/packages/bif-plugin-ledger-connector-quorum/src/main/typescript/plugin-ledger-connector-quorum.ts index dfd846d883..ff5645c368 100644 --- a/packages/bif-plugin-ledger-connector-quorum/src/main/typescript/plugin-ledger-connector-quorum.ts +++ b/packages/bif-plugin-ledger-connector-quorum/src/main/typescript/plugin-ledger-connector-quorum.ts @@ -1,4 +1,4 @@ -import { IPluginLedgerConnector, IWebServiceEndpoint, IPluginWebService } from '@hyperledger-labs/bif-core-api'; +import { IPluginLedgerConnector, IWebServiceEndpoint, IPluginWebService, PluginAspect } from '@hyperledger-labs/bif-core-api'; import { Logger, LoggerProvider } from '@hyperledger-labs/bif-common'; import Web3 from 'web3'; @@ -50,6 +50,14 @@ export class PluginLedgerConnectorQuorum implements IPluginLedgerConnector { return this.web3.eth.sendTransaction(options as any); } diff --git a/packages/bif-test-plugin-ledger-connector-quorum/package.json b/packages/bif-test-plugin-ledger-connector-quorum/package.json index dd5ff10d68..1a9aa0da23 100644 --- a/packages/bif-test-plugin-ledger-connector-quorum/package.json +++ b/packages/bif-test-plugin-ledger-connector-quorum/package.json @@ -54,6 +54,7 @@ "@hyperledger-labs/bif-core-api": "^0.2.0", "@hyperledger-labs/bif-plugin-kv-storage-memory": "0.2.0", "@hyperledger-labs/bif-plugin-ledger-connector-quorum": "^0.2.0", + "@hyperledger-labs/bif-sdk": "0.2.0", "joi": "14.3.1", "web3": "1.2.7", "web3-eth-contract": "1.2.7", diff --git a/packages/bif-test-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/deploy-contract-via-web-service.ts b/packages/bif-test-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/deploy-contract-via-web-service.ts index fe2d4cb082..9972852c62 100644 --- a/packages/bif-test-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/deploy-contract-via-web-service.ts +++ b/packages/bif-test-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/deploy-contract-via-web-service.ts @@ -4,25 +4,27 @@ import { QuorumTestLedger, IQuorumGenesisOptions, IAccount } from '@hyperledger- import HelloWorldContractJson from '../../../../solidity/hello-world-contract/HelloWorld.json'; import { Logger, LoggerProvider } from '@hyperledger-labs/bif-common'; import { Web3EthContract, IQuorumDeployContractOptions, PluginLedgerConnectorQuorum, PluginFactoryLedgerConnector } from '@hyperledger-labs/bif-plugin-ledger-connector-quorum'; -import { ApiServer, ConfigService } from '@hyperledger-labs/bif-cmd-api-server'; -import { IBifApiServerOptions } from '@hyperledger-labs/bif-cmd-api-server/dist/types/main/typescript/config/config-service'; +import { ApiServer, ConfigService, IBifApiServerOptions } from '@hyperledger-labs/bif-cmd-api-server'; +import { ICactusPlugin } from '@hyperledger-labs/bif-core-api'; +import { PluginKVStorageMemory } from '@hyperledger-labs/bif-plugin-kv-storage-memory'; +import { DefaultApi, Configuration } from '@hyperledger-labs/bif-sdk'; const log: Logger = LoggerProvider.getOrCreate({ label: 'test-deploy-contract-via-web-service', level: 'trace' }) tap.test('pulls up API server and deploys contract via REST API', async (assert: any) => { const configService = new ConfigService(); - // const config = configService.getOrCreate(); - const config = configService.newExampleConfigConvict(); - // config.set('bifNodeId', exampleConfig.bifNodeId); - // config.set('storagePluginPackage', exampleConfig.storagePluginPackage); - // config.set('privateKey', exampleConfig.privateKey); - // config.set('publicKey', exampleConfig.publicKey); - config.set('apiCorsDomainCsv', '*'); - config.set('configFile', null); - config.set('apiPort', 0); - const apiServer = new ApiServer({ config }); - await apiServer.start(); + const bifApiServerOptions: IBifApiServerOptions = configService.newExampleConfig(); + bifApiServerOptions.configFile = ''; + bifApiServerOptions.apiCorsDomainCsv = '*'; + bifApiServerOptions.apiPort = 0; + const config = configService.newExampleConfigConvict(bifApiServerOptions); + const plugins: ICactusPlugin[] = []; + const kvStoragePlugin = new PluginKVStorageMemory({ backend: new Map() }); + plugins.push(kvStoragePlugin) + const apiServer = new ApiServer({ config, plugins }); + const out = await apiServer.start(); + log.debug(`ApiServer.started OK:`, out); const quorumTestLedger = new QuorumTestLedger({ containerImageVersion: '1.0.0' }); await quorumTestLedger.start(); @@ -33,6 +35,8 @@ tap.test('pulls up API server and deploys contract via REST API', async (assert: log.debug(`Stopped container OK.`); await quorumTestLedger.destroy(); log.debug(`Destroyed container OK.`); + await apiServer.shutdown(); + log.debug(`ApiServer shut down OK.`); }); const rpcApiHttpHost = await quorumTestLedger.getRpcApiHttpHost(); @@ -66,6 +70,26 @@ tap.test('pulls up API server and deploys contract via REST API', async (assert: log.debug(`Got message from smart contract method:`, { callResponse }); assert.ok(callResponse); + const httpServer = apiServer.getHttpServerApi(); + // AddressInfo={ address: string, family: string, port: number }; + const addressInfo: any = httpServer?.address(); + log.debug(`AddressInfo: `, addressInfo); + const BIF_API_HOST = `http://${addressInfo.address}:${addressInfo.port}`; + const configuration = new Configuration({ basePath: BIF_API_HOST, }); + const api = new DefaultApi(configuration); + const response = await api.apiV1ConsortiumPost({ + configurationEndpoint: 'domain-and-an-http-endpoint', + id: 'asdf', + name: 'asdf', + bifNodes: [ + { + host: 'BIF-NODE-HOST-1', publicKey: 'FAKE-PUBLIC-KEY' + } + ] + }); + assert.ok(response); + assert.ok(response.status > 199 && response.status < 300); + assert.end(); log.debug('Assertion ended OK.'); });