Skip to content

Commit

Permalink
Merge pull request #108 from Chengxuan/multi-ns
Browse files Browse the repository at this point in the history
udpate sandbox UI to support mult-namespaces
  • Loading branch information
dechdev authored Jan 9, 2023
2 parents 8593ca6 + 972ad92 commit ef69648
Show file tree
Hide file tree
Showing 26 changed files with 539 additions and 178 deletions.
26 changes: 13 additions & 13 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"homepage": "https://github.com/hyperledger/firefly-sandbox#readme",
"dependencies": {
"@hyperledger/firefly-sdk": "1.1.0-alpha.1",
"@hyperledger/firefly-sdk": "^1.2.3",
"body-parser": "^1.20.0",
"class-transformer": "^0.3.1",
"class-validator": "^0.12.2",
Expand Down
11 changes: 0 additions & 11 deletions server/src/clients/firefly.ts

This file was deleted.

17 changes: 17 additions & 0 deletions server/src/clients/fireflySDKWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import FireFly from '@hyperledger/firefly-sdk';
import { InternalServerError } from 'routing-controllers';

const fireflySDKs = {};

export const getFireflyClient = (namespace: string = 'default') => {
if (!fireflySDKs[namespace]) {
fireflySDKs[namespace] = new FireFly({
host: process.env.FF_ENDPOINT || 'http://localhost:5000',
namespace,
});
fireflySDKs[namespace].onError((err) => {
throw new InternalServerError(err.message);
});
}
return fireflySDKs[namespace];
};
68 changes: 41 additions & 27 deletions server/src/controllers/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Get, InternalServerError, JsonController, Param, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { firefly } from '../clients/firefly';
import { FFStatus, Organization, Plugin, Plugins, Transaction, Verifier } from '../interfaces';
import { getFireflyClient } from '../clients/fireflySDKWrapper';
import { FFNamespace, Organization, Plugin, Plugins, Transaction, Verifier } from '../interfaces';
const DEFAULT_NAMESPACE = process.env.FF_DEFAULT_NAMESPACE || 'default';

/**
* Common Operations - API Server
*/
Expand All @@ -12,7 +13,11 @@ export class CommonController {
@Get('/organizations')
@ResponseSchema(Organization, { isArray: true })
@OpenAPI({ summary: 'List all organizations in network' })
async organizations(@QueryParam('exclude_self') exclude_self: boolean): Promise<Organization[]> {
async organizations(
@QueryParam('exclude_self') exclude_self: boolean,
@QueryParam('ns') namespace: string,
): Promise<Organization[]> {
const firefly = getFireflyClient(namespace);
let orgs = await firefly.getOrganizations();
if (exclude_self) {
const status = await firefly.getStatus();
Expand All @@ -24,18 +29,20 @@ export class CommonController {
@Get('/organizations/self')
@ResponseSchema(Organization)
@OpenAPI({ summary: 'Look up local organization' })
async self(): Promise<Organization> {
async self(@QueryParam('ns') namespace: string): Promise<Organization> {
const firefly = getFireflyClient(namespace);
const status = await firefly.getStatus();
return { id: status.org?.id, did: status.org?.did, name: status.org?.name};
return { id: status.org?.id, did: status.org?.did, name: status.org?.name };
}

@Get('/verifiers')
@ResponseSchema(Verifier, { isArray: true })
@OpenAPI({ summary: 'List verifiers (such as Ethereum keys) for all organizations in network' })
async verifiers(): Promise<Verifier[]> {
async verifiers(@QueryParam('ns') namespace: string): Promise<Verifier[]> {
const firefly = getFireflyClient(namespace);
try {
const orgs = await firefly.getOrganizations();
let verifiers = await firefly.getVerifiers(DEFAULT_NAMESPACE);
let verifiers = await firefly.getVerifiers();
if (verifiers.length === 0) {
// attempt to query legacy ff_system verifiers
verifiers = await firefly.getVerifiers('ff_system');
Expand All @@ -49,7 +56,7 @@ export class CommonController {
}
return result;
} catch (err) {
if (err.message == "FF10187: Namespace does not exist") {
if (err.message == 'FF10187: Namespace does not exist') {
return [];
}
throw new InternalServerError(err.message);
Expand All @@ -59,9 +66,10 @@ export class CommonController {
@Get('/verifiers/self')
@ResponseSchema(Verifier, { isArray: true })
@OpenAPI({ summary: 'List verifiers (such as Ethereum keys) for local organization' })
async verifierSelf(): Promise<Verifier[]> {
async verifierSelf(@QueryParam('ns') namespace: string): Promise<Verifier[]> {
const firefly = getFireflyClient(namespace);
const status = await firefly.getStatus();
const verifiers = await firefly.getVerifiers(DEFAULT_NAMESPACE);
const verifiers = await firefly.getVerifiers();
const result: Verifier[] = [];
for (const v of verifiers) {
if (status.org?.id === v.identity) {
Expand All @@ -74,7 +82,8 @@ export class CommonController {
@Get('/plugins')
@ResponseSchema(Plugins)
@OpenAPI({ summary: 'List plugins on the FireFly node' })
async plugins(): Promise<Plugins> {
async plugins(@QueryParam('ns') namespace: string): Promise<Plugins> {
const firefly = getFireflyClient(namespace);
const status = await firefly.getStatus();
return {
blockchain: status.plugins.blockchain as Plugin[],
Expand All @@ -85,30 +94,35 @@ export class CommonController {
@Get('/transactions/:id')
@ResponseSchema(Transaction)
@OpenAPI({ summary: 'Look up a FireFly transaction by ID' })
async transaction(@Param('id') id: string): Promise<Transaction> {
async transaction(
@Param('id') id: string,
@QueryParam('ns') namespace: string,
): Promise<Transaction> {
const firefly = getFireflyClient(namespace);
const tx = await firefly.getTransaction(id);
return {
id: tx.id,
type: tx.type,
};
}

@Get('/firefly/status')
@ResponseSchema(FFStatus)
@Get('/firefly/namespaces')
@ResponseSchema(FFNamespace, { isArray: true })
@OpenAPI({ summary: 'Look up FireFly status' })
async ffStatus(): Promise<FFStatus> {
const status = await firefly.getStatus();
if ("multiparty" in status) {
return {
multiparty: status.multiparty?.enabled,
namespace: DEFAULT_NAMESPACE,
};
} else {
// Assume multiparty mode if `multiparty` key is missing from status
return {
multiparty: true,
namespace: DEFAULT_NAMESPACE,
}
async ffNamespaces(): Promise<FFNamespace[] | undefined> {
const firefly = getFireflyClient();
const namespaces = await firefly.getNamespaces();
const namespaceStatuses = [];
for (let i = 0; i < namespaces.length; i++) {
const ns = namespaces[i];
const nsFirefly = getFireflyClient(ns.name);
const status = await nsFirefly.getStatus();
namespaceStatuses.push({
multiparty: status.multiparty ? status.multiparty.enabled : true,
name: ns.name,
default: ns.name === DEFAULT_NAMESPACE,
});
}
return namespaceStatuses.sort((a, b) => (a.name > b.name ? 1 : a.name < b.name ? -1 : 0));
}
}
51 changes: 40 additions & 11 deletions server/src/controllers/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
Get,
Param,
NotFoundError,
QueryParam,
} from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { firefly } from '../clients/firefly';
import { getFireflyClient } from '../clients/fireflySDKWrapper';
import { formatTemplate, quoteAndEscape as q } from '../utils';
import {
AsyncResponse,
Expand Down Expand Up @@ -36,7 +37,11 @@ export class ContractsController {
'Schema may be in <a href="https://hyperledger.github.io/firefly/reference/firefly_interface_format">FFI</a> ' +
'or <a href="https://docs.ethers.io/v5/api/utils/abi/formats/#abi-formats--solidity">Solidity JSON ABI</a> format. ',
})
async createInterface(@Body() body: ContractInterface): Promise<AsyncResponse> {
async createInterface(
@Body() body: ContractInterface,
@QueryParam('ns') namespace: string,
): Promise<AsyncResponse> {
const firefly = getFireflyClient(namespace);
// See ContractsTemplateController and keep template code up to date.
const ffi =
body.format === ContractInterfaceFormat.ABI
Expand All @@ -54,7 +59,11 @@ export class ContractsController {
@HttpCode(202)
@ResponseSchema(AsyncResponse)
@OpenAPI({ summary: 'Define a new contract API' })
async createAPI(@Body() body: ContractAPI): Promise<AsyncResponse> {
async createAPI(
@Body() body: ContractAPI,
@QueryParam('ns') namespace: string,
): Promise<AsyncResponse> {
const firefly = getFireflyClient(namespace);
// See ContractsTemplateController and keep template code up to date.
const api = await firefly.createContractAPI({
name: body.name,
Expand All @@ -73,7 +82,11 @@ export class ContractsController {
@HttpCode(202)
@ResponseSchema(AsyncResponse)
@OpenAPI({ summary: 'Define a new contract API with Fabric' })
async createAPIFabric(@Body() body: ContractAPI): Promise<AsyncResponse> {
async createAPIFabric(
@Body() body: ContractAPI,
@QueryParam('ns') namespace: string,
): Promise<AsyncResponse> {
const firefly = getFireflyClient(namespace);
// See ContractsTemplateController and keep template code up to date.
const api = await firefly.createContractAPI({
name: body.name,
Expand All @@ -92,15 +105,19 @@ export class ContractsController {
@Get('/interface')
@ResponseSchema(ContractInterfaceLookup, { isArray: true })
@OpenAPI({ summary: 'List contract interfaces' })
async getContractInterfaces(): Promise<ContractInterfaceLookup[]> {
async getContractInterfaces(
@QueryParam('ns') namespace: string,
): Promise<ContractInterfaceLookup[]> {
const firefly = getFireflyClient(namespace);
const interfaces = await firefly.getContractInterfaces();
return interfaces;
}

@Get('/api')
@ResponseSchema(ContractAPILookup, { isArray: true })
@OpenAPI({ summary: 'List contract APIs' })
async getAPIs(): Promise<ContractAPILookup[]> {
async getAPIs(@QueryParam('ns') namespace: string): Promise<ContractAPILookup[]> {
const firefly = getFireflyClient(namespace);
const apis = await firefly.getContractAPIs();
return apis.map((api) => ({
name: api.name,
Expand All @@ -112,7 +129,11 @@ export class ContractsController {
@Get('/api/:name')
@ResponseSchema(ContractAPILookup)
@OpenAPI({ summary: 'Get contract API details' })
async getAPI(@Param('name') name: string): Promise<ContractAPILookup> {
async getAPI(
@Param('name') name: string,
@QueryParam('ns') namespace: string,
): Promise<ContractAPILookup> {
const firefly = getFireflyClient(namespace);
const api = await firefly.getContractAPI(name);
if (api === undefined) {
throw new NotFoundError();
Expand All @@ -129,7 +150,11 @@ export class ContractsController {
@Get('/api/:name/listener')
@ResponseSchema(ContractListenerLookup, { isArray: true })
@OpenAPI({ summary: 'List contract API listeners' })
async getAPIListeners(@Param('name') name: string): Promise<ContractListenerLookup[]> {
async getAPIListeners(
@Param('name') name: string,
@QueryParam('ns') namespace: string,
): Promise<ContractListenerLookup[]> {
const firefly = getFireflyClient(namespace);
const api = await firefly.getContractAPI(name);
const listeners = await firefly.getContractListeners({
interface: api.interface.id,
Expand All @@ -147,14 +172,18 @@ export class ContractsController {
@Post('/listener')
@ResponseSchema(ContractListenerLookup)
@OpenAPI({ summary: 'Create a new contract listener' })
async createListener(@Body() body: ContractListener): Promise<ContractListenerLookup> {
async createListener(
@Body() body: ContractListener,
@QueryParam('ns') namespace: string,
): Promise<ContractListenerLookup> {
const firefly = getFireflyClient(namespace);
// See ContractsTemplateController and keep template code up to date.
const listener = await firefly.createContractAPIListener(body.apiName, body.eventPath, {
topic: body.topic,
name: body.name,
options: {
firstEvent: body.firstEvent
}
firstEvent: body.firstEvent,
},
});
return {
id: listener.id,
Expand Down
14 changes: 11 additions & 3 deletions server/src/controllers/datatypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
JsonController,
Param,
NotFoundError,
QueryParam,
} from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { firefly } from '../clients/firefly';
import { getFireflyClient } from '../clients/fireflySDKWrapper';
import { AsyncResponse, DatatypeInterface } from '../interfaces';
import { formatTemplate, quoteAndEscape as q } from '../utils';

Expand All @@ -21,7 +22,8 @@ export class DatatypesController {
@Get()
@ResponseSchema(DatatypeInterface, { isArray: true })
@OpenAPI({ summary: 'List all datatypes' })
async getAllDatatypes(): Promise<DatatypeInterface[]> {
async getAllDatatypes(@QueryParam('ns') namespace: string): Promise<DatatypeInterface[]> {
const firefly = getFireflyClient(namespace);
const datatypes = await firefly.getDatatypes();
return datatypes.map((d) => ({ id: d.id, name: d.name, version: d.version, schema: d.value }));
}
Expand All @@ -32,7 +34,9 @@ export class DatatypesController {
async getAPI(
@Param('name') name: string,
@Param('version') version: string,
@QueryParam('ns') namespace: string,
): Promise<DatatypeInterface> {
const firefly = getFireflyClient(namespace);
const datatype = await firefly.getDatatype(name, version);
if (datatype === undefined) {
throw new NotFoundError();
Expand All @@ -49,7 +53,11 @@ export class DatatypesController {
@HttpCode(202)
@ResponseSchema(AsyncResponse)
@OpenAPI({ summary: 'Creates and broadcasts a new datatype' })
async createDatatype(@Body() body: DatatypeInterface): Promise<AsyncResponse> {
async createDatatype(
@Body() body: DatatypeInterface,
@QueryParam('ns') namespace: string,
): Promise<AsyncResponse> {
const firefly = getFireflyClient(namespace);
// See DatatypesTemplateController and keep template code up to date.
const datatype = await firefly.createDatatype({
name: body.name,
Expand Down
Loading

0 comments on commit ef69648

Please sign in to comment.