Simplify fabric code

Attempt to make fabric code easier to document

Signed-off-by: James Taylor <jamest@uk.ibm.com>
This commit is contained in:
James Taylor 2021-08-16 17:36:37 +01:00
parent 862080773e
commit 45683b2a1a
9 changed files with 274 additions and 234 deletions

View file

@ -3,10 +3,14 @@
*/ */
import { mock } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended';
import { Contract, Network, Transaction, WalletStore } from 'fabric-network'; import { Contract, Network, Transaction } from 'fabric-network';
import { mocked } from 'ts-jest/utils'; import { mocked } from 'ts-jest/utils';
import * as fabricProtos from 'fabric-protos'; import * as fabricProtos from 'fabric-protos';
const actualFabricNetwork = jest.requireActual('fabric-network');
const Wallet = actualFabricNetwork.Wallet;
const Wallets = actualFabricNetwork.Wallets;
const mockAsset1 = { const mockAsset1 = {
ID: 'asset1', ID: 'asset1',
Color: 'blue', Color: 'blue',
@ -50,15 +54,8 @@ const {
DefaultEventHandlerStrategies, DefaultEventHandlerStrategies,
DefaultQueryHandlerStrategies, DefaultQueryHandlerStrategies,
Gateway, Gateway,
Wallet,
Wallets,
}: FabricNetworkModule = jest.createMockFromModule('fabric-network'); }: FabricNetworkModule = jest.createMockFromModule('fabric-network');
const mockWalletStore = mock<WalletStore>();
mocked(Wallets.newInMemoryWallet).mockResolvedValue(
new Wallet(mockWalletStore)
);
const mockAssetExistsTransaction = mock<Transaction>(); const mockAssetExistsTransaction = mock<Transaction>();
mockAssetExistsTransaction.evaluate mockAssetExistsTransaction.evaluate
.calledWith('asset1') .calledWith('asset1')
@ -171,24 +168,11 @@ mockNetwork.getContract.calledWith('qscc').mockReturnValue(mockSystemContract);
mocked(Gateway.prototype.getNetwork).mockResolvedValue(mockNetwork); mocked(Gateway.prototype.getNetwork).mockResolvedValue(mockNetwork);
// TODO remove this and use simpler mocks in fabric spec tests
const getMockedNetwork = (getContract = jest.fn()) => {
return mocked(Gateway.prototype.getNetwork).mockResolvedValue({
getGateway: jest.fn(),
getContract,
getChannel: jest.fn(),
addCommitListener: jest.fn(),
removeCommitListener: jest.fn(),
addBlockListener: jest.fn(),
removeBlockListener: jest.fn(),
});
};
export { export {
DefaultEventHandlerStrategies, DefaultEventHandlerStrategies,
DefaultQueryHandlerStrategies, DefaultQueryHandlerStrategies,
Contract, Contract,
Gateway, Gateway,
Wallet,
Wallets, Wallets,
getMockedNetwork,
}; };

View file

@ -16,11 +16,7 @@ import { Contract } from 'fabric-network';
import { getReasonPhrase, StatusCodes } from 'http-status-codes'; import { getReasonPhrase, StatusCodes } from 'http-status-codes';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import { AssetExistsError, AssetNotFoundError } from './errors'; import { AssetExistsError, AssetNotFoundError } from './errors';
import { import { evatuateTransaction, submitTransaction } from './fabric';
evatuateTransaction,
submitTransaction,
getContractForOrg,
} from './fabric';
import { logger } from './logger'; import { logger } from './logger';
const { const {
@ -38,7 +34,9 @@ assetsRouter.get('/', async (req: Request, res: Response) => {
logger.debug('Get all assets request received'); logger.debug('Get all assets request received');
try { try {
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const contract = req.app.get(mspId).assetContract as Contract;
const data = await evatuateTransaction(contract, 'GetAllAssets'); const data = await evatuateTransaction(contract, 'GetAllAssets');
let assets = []; let assets = [];
if (data.length > 0) { if (data.length > 0) {
@ -77,8 +75,9 @@ assetsRouter.post(
}); });
} }
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const redis: Redis = req.app.get('redis'); const contract = req.app.get(mspId).assetContract as Contract;
const redis = req.app.get('redis') as Redis;
const assetId = req.body.id; const assetId = req.body.id;
try { try {
@ -128,7 +127,8 @@ assetsRouter.options('/:assetId', async (req: Request, res: Response) => {
logger.debug('Asset options request received for asset ID %s', assetId); logger.debug('Asset options request received for asset ID %s', assetId);
try { try {
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const contract = req.app.get(mspId).assetContract as Contract;
const data = await evatuateTransaction(contract, 'AssetExists', assetId); const data = await evatuateTransaction(contract, 'AssetExists', assetId);
const exists = data.toString() === 'true'; const exists = data.toString() === 'true';
@ -167,7 +167,8 @@ assetsRouter.get('/:assetId', async (req: Request, res: Response) => {
logger.debug('Read asset request received for asset ID %s', assetId); logger.debug('Read asset request received for asset ID %s', assetId);
try { try {
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const contract = req.app.get(mspId).assetContract as Contract;
const data = await evatuateTransaction(contract, 'ReadAsset', assetId); const data = await evatuateTransaction(contract, 'ReadAsset', assetId);
const asset = JSON.parse(data.toString()); const asset = JSON.parse(data.toString());
@ -225,8 +226,9 @@ assetsRouter.put(
}); });
} }
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const redis: Redis = req.app.get('redis'); const contract = req.app.get(mspId).assetContract as Contract;
const redis = req.app.get('redis') as Redis;
const assetId = req.params.assetId; const assetId = req.params.assetId;
try { try {
@ -294,8 +296,9 @@ assetsRouter.patch(
}); });
} }
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const redis: Redis = req.app.get('redis'); const contract = req.app.get(mspId).assetContract as Contract;
const redis = req.app.get('redis') as Redis;
const assetId = req.params.assetId; const assetId = req.params.assetId;
const newOwner = req.body[0].value; const newOwner = req.body[0].value;
@ -339,8 +342,9 @@ assetsRouter.patch(
assetsRouter.delete('/:assetId', async (req: Request, res: Response) => { assetsRouter.delete('/:assetId', async (req: Request, res: Response) => {
logger.debug(req.body, 'Delete asset request received'); logger.debug(req.body, 'Delete asset request received');
const contract: Contract = getContractForOrg(req).contract; const mspId = req.user as string;
const redis: Redis = req.app.get('redis'); const contract = req.app.get(mspId).assetContract as Contract;
const redis = req.app.get('redis') as Redis;
const assetId = req.params.assetId; const assetId = req.params.assetId;
try { try {

View file

@ -17,16 +17,13 @@ export const fabricAPIKeyStrategy: HeaderAPIKeyStrategy =
false, false,
function (apikey, done) { function (apikey, done) {
logger.debug({ apikey }, 'Checking X-API-Key'); logger.debug({ apikey }, 'Checking X-API-Key');
const user: { org: string } = {
org: '',
};
if (apikey === config.org1ApiKey) { if (apikey === config.org1ApiKey) {
user.org = config.identityNameOrg1; const user = config.mspIdOrg1;
logger.debug('Organisation set to Org1'); logger.debug('User set to %s', user);
done(null, user); done(null, user);
} else if (apikey === config.org2ApiKey) { } else if (apikey === config.org2ApiKey) {
user.org = config.identityNameOrg2; const user = config.mspIdOrg2;
logger.info('Organisation set to Org2'); logger.debug('User set to %s', user);
done(null, user); done(null, user);
} else { } else {
logger.debug({ apikey }, 'No valid X-API-Key'); logger.debug({ apikey }, 'No valid X-API-Key');
@ -44,7 +41,6 @@ export const authenticateApiKey = (
'headerapikey', 'headerapikey',
{ session: false }, { session: false },
(err, user, _info) => { (err, user, _info) => {
logger.debug({ user }, 'USERUSERUSER');
if (err) return next(err); if (err) return next(err);
if (!user) if (!user)
return res.status(UNAUTHORIZED).json({ return res.status(UNAUTHORIZED).json({

View file

@ -50,12 +50,6 @@ export const asLocalhost = env
.example('true') .example('true')
.asBoolStrict(); .asBoolStrict();
// TODO delete this and use mspIdOrg1
export const identityNameOrg1 = 'Org1';
// TODO delete this and use mspIdOrg2
export const identityNameOrg2 = 'Org2';
/* /*
* The Org1 MSP ID * The Org1 MSP ID
*/ */
@ -128,7 +122,7 @@ export const connectionProfileOrg1 = env
.example( .example(
'{"name":"test-network-org1","version":"1.0.0","client":{"organization":"Org1" ... }' '{"name":"test-network-org1","version":"1.0.0","client":{"organization":"Org1" ... }'
) )
.asJsonObject(); .asJsonObject() as Record<string, unknown>;
/* /*
* Certificate for the Org1 identity * Certificate for the Org1 identity
@ -157,7 +151,7 @@ export const connectionProfileOrg2 = env
.example( .example(
'{"name":"test-network-org2","version":"1.0.0","client":{"organization":"Org2" ... }' '{"name":"test-network-org2","version":"1.0.0","client":{"organization":"Org2" ... }'
) )
.asJsonObject(); .asJsonObject() as Record<string, unknown>;
/* /*
* Certificate for the Org2 identity * Certificate for the Org2 identity

View file

@ -1,13 +1,32 @@
import { retryTransaction, getGateway } from './fabric'; /*
import { getMockedNetwork } from './__mocks__/fabric-network'; * SPDX-License-Identifier: Apache-2.0
*/
import {
createGateway,
createWallet,
getContracts,
getNetwork,
retryTransaction,
} from './fabric';
import * as config from './config'; import * as config from './config';
import IORedis from './__mocks__/IORedis'; import IORedis from './__mocks__/IORedis';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import { Contract } from 'fabric-network'; import {
Contract,
Gateway,
GatewayOptions,
Network,
Transaction,
Wallet,
} from 'fabric-network';
import { mock } from 'jest-mock-extended';
jest.mock('./config'); jest.mock('./config');
jest.mock('ioredis'); jest.mock('ioredis');
const redisOptions = { const redisOptions = {
port: config.redisPort, port: config.redisPort,
host: config.redisHost, host: config.redisHost,
@ -17,20 +36,72 @@ const redisOptions = {
const redis = new IORedis(redisOptions) as unknown as Redis; const redis = new IORedis(redisOptions) as unknown as Redis;
describe('Testing retryTransaction', () => { describe('Fabric', () => {
const transactionId = describe('createWallet', () => {
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95'; it('creates a wallet containing identities for both orgs', async () => {
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`; const wallet = await createWallet();
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
const savedTransaction = {
timestamp: timestamp.toString(),
state: state,
retries: '',
args: args,
};
describe('Check retry increment ', () => { expect(await wallet.list()).toStrictEqual(['Org1MSP', 'Org2MSP']);
});
});
describe('createGateway', () => {
it('creates a Gateway and connects using the provided arguments', async () => {
const connectionProfile = config.connectionProfileOrg1;
const identity = config.mspIdOrg1;
const mockWallet = mock<Wallet>();
const gateway = await createGateway(
connectionProfile,
identity,
mockWallet
);
expect(gateway.connect).toBeCalledWith(
connectionProfile,
expect.objectContaining<GatewayOptions>({
wallet: mockWallet,
identity,
discovery: expect.any(Object),
eventHandlerOptions: expect.any(Object),
queryHandlerOptions: expect.any(Object),
})
);
});
});
describe('getNetwork', () => {
it('gets a Network instance for the required channel from the Gateway', async () => {
const mockGateway = mock<Gateway>();
await getNetwork(mockGateway);
expect(mockGateway.getNetwork).toHaveBeenCalledWith(config.channelName);
});
});
describe('getContracts', () => {
it('gets the asset and qscc contracts from the network', async () => {
const mockBasicContract = mock<Contract>();
const mockSystemContract = mock<Contract>();
const mockNetwork = mock<Network>();
mockNetwork.getContract
.calledWith(config.chaincodeName)
.mockReturnValue(mockBasicContract);
mockNetwork.getContract
.calledWith('qscc')
.mockReturnValue(mockSystemContract);
const contracts = await getContracts(mockNetwork);
expect(contracts).toStrictEqual({
assetContract: mockBasicContract,
qsccContract: mockSystemContract,
});
});
});
describe('Testing retryTransaction', () => {
const transactionId = const transactionId =
'0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95'; '0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`; const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
@ -43,52 +114,53 @@ describe('Testing retryTransaction', () => {
args: args, args: args,
}; };
it('Transaction failure, check redis increment func call', async () => { describe('Check retry increment ', () => {
jest.doMock('fabric-network'); const transactionId =
const transaction = { '0ae62c01e4c4b112c3f3954a2f11243da76778e46df9ad2783bcbafc79652b95';
submit: jest.fn().mockRejectedValue({}), const state = `{"name":"CreateAsset","nonce":"damqinq8nrI4n4qY8lFVsZw7RwG2ufrv","transactionId":${transactionId}`;
const args = '["test111","red",400,"Jean",101]';
const timestamp = 1628078044362;
const savedTransaction = {
timestamp: timestamp.toString(),
state: state,
retries: '',
args: args,
}; };
const mockedContact = {
deserializeTransaction: jest.fn().mockReturnValue(transaction),
};
const rejectableGetContract = jest
.fn()
.mockImplementation(() => mockedContact);
const network = getMockedNetwork(rejectableGetContract)(''); it('Transaction failure, check redis increment func call', async () => {
const contract: Contract = (await network).getContract(''); const mockTransaction = mock<Transaction>();
savedTransaction.retries = '3'; mockTransaction.submit.mockRejectedValue('MOCKERROR');
await retryTransaction(contract, redis, transactionId, savedTransaction); const mockContract = mock<Contract>();
expect(redis.hincrby).toHaveBeenCalledTimes(1); mockContract.deserializeTransaction.mockReturnValue(mockTransaction);
savedTransaction.retries = '3';
await retryTransaction(
mockContract,
redis,
transactionId,
savedTransaction
);
expect(redis.hincrby).toHaveBeenCalledTimes(1);
});
}); });
});
describe('Transaction successful, check redis delete key func call ', () => { describe('Transaction successful, check redis delete key func call ', () => {
it('call redis increment', async () => { it('call redis increment', async () => {
jest.doMock('fabric-network'); const mockTransaction = mock<Transaction>();
const transaction = { mockTransaction.submit.mockResolvedValue(Buffer.from('{}'));
submit: jest.fn().mockResolvedValue({}), const mockContract = mock<Contract>();
}; mockContract.deserializeTransaction.mockReturnValue(mockTransaction);
const mockedContact = {
deserializeTransaction: jest.fn().mockReturnValue(transaction),
};
const resolvableGetContract = jest
.fn()
.mockImplementation(() => mockedContact);
const network = getMockedNetwork(resolvableGetContract)(''); savedTransaction.retries = '3';
const contract: Contract = (await network).getContract(''); await retryTransaction(
savedTransaction.retries = '3'; mockContract,
await retryTransaction(contract, redis, transactionId, savedTransaction); redis,
expect(redis.del).toHaveBeenCalledTimes(1); transactionId,
savedTransaction
);
expect(redis.del).toHaveBeenCalledTimes(1);
});
}); });
}); });
}); });
describe('Test getGateway', () => {
it('should throw error for invalid org name', async () => {
expect(async () => await getGateway('')).rejects.toThrow(
'Invalid org name for gateway'
);
});
});

View file

@ -13,8 +13,8 @@ import {
BlockListener, BlockListener,
BlockEvent, BlockEvent,
TransactionEvent, TransactionEvent,
Wallet,
} from 'fabric-network'; } from 'fabric-network';
import { Request } from 'express';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import * as config from './config'; import * as config from './config';
import { logger } from './logger'; import { logger } from './logger';
@ -31,63 +31,60 @@ import {
} from './errors'; } from './errors';
import protos from 'fabric-protos'; import protos from 'fabric-protos';
export const getNetwork = async (gateway: Gateway): Promise<Network> => { /*
const network = await gateway.getNetwork(config.channelName); * Creates an in memory wallet to hold credentials for an Org1 and Org2 user
return network; *
}; * In this sample there is a single user for each MSP ID to demonstrate how
* a client app might submit transactions for different users
interface FabricConfigType { *
identityName: string; * Alternatively a REST server could use its own identity for all transactions,
mspId: string; * or it could use credentials supplied in the REST requests
connectionProfile: { [key: string]: any }; */
certificate: string; export const createWallet = async (): Promise<Wallet> => {
privateKey: string;
}
const ORG1_CONFIG = {
identityName: config.identityNameOrg1,
mspId: config.mspIdOrg1,
connectionProfile: config.connectionProfileOrg1,
certificate: config.certificateOrg1,
privateKey: config.privateKeyOrg1,
};
const ORG2_CONFIG = {
identityName: config.identityNameOrg2,
mspId: config.mspIdOrg2,
connectionProfile: config.connectionProfileOrg2,
certificate: config.certificateOrg2,
privateKey: config.privateKeyOrg2,
};
const FabricDataMapper: { [key: string]: FabricConfigType } = {
[config.identityNameOrg1]: ORG1_CONFIG,
[config.identityNameOrg2]: ORG2_CONFIG,
};
export const getGateway = async (org: string): Promise<Gateway> => {
const fabricConfig = FabricDataMapper[org];
if (fabricConfig == undefined) {
throw new Error('Invalid org name for gateway');
}
logger.debug('Configuring fabric gateway for %s', org);
const wallet = await Wallets.newInMemoryWallet(); const wallet = await Wallets.newInMemoryWallet();
const x509Identity = { const org1Identity = {
credentials: { credentials: {
certificate: fabricConfig.certificate, certificate: config.certificateOrg1,
privateKey: fabricConfig.privateKey, privateKey: config.privateKeyOrg1,
}, },
mspId: fabricConfig.mspId, mspId: config.mspIdOrg1,
type: 'X.509', type: 'X.509',
}; };
await wallet.put(fabricConfig.identityName, x509Identity); await wallet.put(config.mspIdOrg1, org1Identity);
const org2Identity = {
credentials: {
certificate: config.certificateOrg2,
privateKey: config.privateKeyOrg2,
},
mspId: config.mspIdOrg2,
type: 'X.509',
};
await wallet.put(config.mspIdOrg2, org2Identity);
return wallet;
};
/*
* Create a Gateway connection
*
* Gateway instances can and should be reused rather than connecting to submit every transaction
*/
export const createGateway = async (
connectionProfile: Record<string, unknown>,
identity: string,
wallet: Wallet
): Promise<Gateway> => {
logger.debug({ connectionProfile, identity }, 'Configuring gateway');
const gateway = new Gateway(); const gateway = new Gateway();
const connectOptions: GatewayOptions = { const options: GatewayOptions = {
wallet, wallet,
identity: fabricConfig.identityName, identity,
discovery: { enabled: true, asLocalhost: config.asLocalhost }, discovery: { enabled: true, asLocalhost: config.asLocalhost },
eventHandlerOptions: { eventHandlerOptions: {
commitTimeout: config.commitTimeout, commitTimeout: config.commitTimeout,
@ -100,16 +97,22 @@ export const getGateway = async (org: string): Promise<Gateway> => {
}, },
}; };
await gateway.connect(fabricConfig.connectionProfile, connectOptions); await gateway.connect(connectionProfile, options);
return gateway; return gateway;
}; };
export const getNetwork = async (gateway: Gateway): Promise<Network> => {
const network = await gateway.getNetwork(config.channelName);
return network;
};
export const getContracts = async ( export const getContracts = async (
network: Network network: Network
): Promise<{ contract: Contract; qscc: Contract }> => { ): Promise<{ assetContract: Contract; qsccContract: Contract }> => {
const contract = network.getContract(config.chaincodeName); const assetContract = network.getContract(config.chaincodeName);
const qscc = network.getContract('qscc'); const qsccContract = network.getContract('qscc');
return { contract, qscc }; return { assetContract, qsccContract };
}; };
export const startRetryLoop = (contract: Contract, redis: Redis): void => { export const startRetryLoop = (contract: Contract, redis: Redis): void => {
@ -334,25 +337,15 @@ export const blockEventHandler = (redis: Redis): BlockListener => {
return blockListner; return blockListner;
}; };
export const getChainInfo = async (qscc: Contract): Promise<boolean> => { export const getBlockHeight = async (
try { qscc: Contract
const data = await qscc.evaluateTransaction( ): Promise<number | Long.Long> => {
'GetChainInfo', const data = await qscc.evaluateTransaction(
config.channelName 'GetChainInfo',
); config.channelName
const info = protos.common.BlockchainInfo.decode(data); );
const blockHeight = info.height.toString(); const info = protos.common.BlockchainInfo.decode(data);
logger.info('Current block height: %s', blockHeight); const blockHeight = info.height;
return true; logger.debug('Current block height: %d', blockHeight);
} catch (e) { return blockHeight;
logger.error(e, 'Unable to get blockchain info');
return false;
}
};
export const getContractForOrg = (
req: Request
): { contract: Contract; qscc: Contract } => {
const user: { org: string } = req.user as { org: string };
return req.app.get('fabric')[user.org as string].contracts;
}; };

View file

@ -12,10 +12,11 @@ import { createServer } from './server';
async function main() { async function main() {
const app = await createServer(); const app = await createServer();
const contract: Contract = // TODO block listener and retry logic currently only handles a single org!!!
app.get('fabric')[config.identityNameOrg1].contracts.contract; // TODO should these be initialised here?
const redis: Redis = app.get('redis'); const contract = app.get(config.mspIdOrg1).assetContract as Contract;
const network: Network = app.get('fabric')[config.identityNameOrg1].network; const redis = app.get('redis') as Redis;
const network = app.get('networkOrg1') as Network;
await network.addBlockListener(blockEventHandler(redis)); await network.addBlockListener(blockEventHandler(redis));
startRetryLoop(contract, redis); startRetryLoop(contract, redis);

View file

@ -12,10 +12,10 @@ import { assetsRouter } from './assets.router';
import { transactionsRouter } from './transactions.router'; import { transactionsRouter } from './transactions.router';
import { import {
getContracts, getContracts,
getGateway,
getNetwork, getNetwork,
getChainInfo, getBlockHeight,
getContractForOrg, createGateway,
createWallet,
} from './fabric'; } from './fabric';
import { redis } from './redis'; import { redis } from './redis';
import { Contract } from 'fabric-network'; import { Contract } from 'fabric-network';
@ -74,29 +74,29 @@ export const createServer = async (): Promise<Application> => {
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
app.use(helmet()); app.use(helmet());
} }
//
const gatewayOrg1 = await getGateway(config.identityNameOrg1); const wallet = await createWallet();
const gatewayOrg2 = await getGateway(config.identityNameOrg2);
const gatewayOrg1 = await createGateway(
config.connectionProfileOrg1,
config.mspIdOrg1,
wallet
);
const networkOrg1 = await getNetwork(gatewayOrg1); const networkOrg1 = await getNetwork(gatewayOrg1);
const networkOrg2 = await getNetwork(gatewayOrg2);
const contractsOrg1 = await getContracts(networkOrg1); const contractsOrg1 = await getContracts(networkOrg1);
app.set(config.mspIdOrg1, contractsOrg1);
// TODO used for block listener, which needs fixing!
app.set('networkOrg1', networkOrg1);
const gatewayOrg2 = await createGateway(
config.connectionProfileOrg2,
config.mspIdOrg2,
wallet
);
const networkOrg2 = await getNetwork(gatewayOrg2);
const contractsOrg2 = await getContracts(networkOrg2); const contractsOrg2 = await getContracts(networkOrg2);
app.set(config.mspIdOrg2, contractsOrg2);
const fabric = {
[config.identityNameOrg1]: {
gateway: gatewayOrg1,
contracts: contractsOrg1,
network: networkOrg1,
},
[config.identityNameOrg2]: {
gateway: gatewayOrg2,
contracts: contractsOrg2,
network: networkOrg2,
},
};
app.set('fabric', fabric);
app.set('redis', redis); app.set('redis', redis);
@ -107,32 +107,27 @@ export const createServer = async (): Promise<Application> => {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}) })
); );
app.get('/live', async (_req, res) => { app.get('/live', async (req, res) => {
_req.user = { org: config.identityNameOrg1 }; logger.debug(req.body, 'Liveness request received');
const qsccOrg1: Contract = getContractForOrg(_req).qscc;
const Org1Liveness = await getChainInfo(qsccOrg1); const qsccOrg1 = req.app.get(config.mspIdOrg1).qsccContract as Contract;
logger.debug('Org1 liveness %s', Org1Liveness); const qsccOrg2 = req.app.get(config.mspIdOrg2).qsccContract as Contract;
_req.user = { org: config.identityNameOrg2 };
const qsccOrg2: Contract = getContractForOrg(_req).qscc; try {
const Org2Liveness = await getChainInfo(qsccOrg2); await Promise.all([getBlockHeight(qsccOrg1), getBlockHeight(qsccOrg2)]);
logger.debug('Org2 liveness %s', Org2Liveness); } catch (err) {
logger.error(err, 'Error processing liveness request');
if (Org1Liveness && Org2Liveness) {
res.status(OK).json({
status: getReasonPhrase(OK),
timestamp: new Date().toISOString(),
});
} else {
res.status(SERVICE_UNAVAILABLE).json({ res.status(SERVICE_UNAVAILABLE).json({
status: getReasonPhrase(SERVICE_UNAVAILABLE), status: getReasonPhrase(SERVICE_UNAVAILABLE),
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}); });
} }
});
// TODO delete me res.status(OK).json({
app.get('/error', (_req, _res) => { status: getReasonPhrase(OK),
throw new Error('Example error'); timestamp: new Date().toISOString(),
});
}); });
app.use('/api/assets', authenticateApiKey, assetsRouter); app.use('/api/assets', authenticateApiKey, assetsRouter);

View file

@ -7,7 +7,7 @@ import { Contract } from 'fabric-network';
import { protos } from 'fabric-protos'; import { protos } from 'fabric-protos';
import { getReasonPhrase, StatusCodes } from 'http-status-codes'; import { getReasonPhrase, StatusCodes } from 'http-status-codes';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import { evatuateTransaction, getContractForOrg } from './fabric'; import { evatuateTransaction } from './fabric';
import { logger } from './logger'; import { logger } from './logger';
import * as config from './config'; import * as config from './config';
import { TransactionNotFoundError } from './errors'; import { TransactionNotFoundError } from './errors';
@ -28,8 +28,9 @@ transactionsRouter.get(
let progress: Progress = 'DONE'; let progress: Progress = 'DONE';
let validationCode = ''; let validationCode = '';
const qscc: Contract = getContractForOrg(req).qscc; const mspId = req.user as string;
const redis: Redis = req.app.get('redis'); const qscc = req.app.get(mspId).qsccContract as Contract;
const redis = req.app.get('redis') as Redis;
try { try {
const savedTransaction = await (redis as Redis).hgetall( const savedTransaction = await (redis as Redis).hgetall(