Fixes to TypeScript asset-transfer-events to match Go sample (#619)

Changed:
- Console output formatting
- Transaction arguments
- Readability and comments of code
- Linting rules
- Missing semicolons
- Package name and descrption

Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
This commit is contained in:
Mark S. Lewis 2022-02-03 15:13:54 +00:00 committed by GitHub
parent 975c9773bd
commit a492d7d241
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 76 deletions

View file

@ -1,7 +1,7 @@
{ {
"env": { "env": {
"node": true, "node": true,
"es6": true "es2020": true
}, },
"root": true, "root": true,
"ignorePatterns": [ "ignorePatterns": [
@ -30,15 +30,16 @@
"sourceType": "module", "sourceType": "module",
"ecmaFeatures": { "ecmaFeatures": {
"impliedStrict": true "impliedStrict": true
} },
"project": "./tsconfig.json"
}, },
"plugins": [ "plugins": [
"@typescript-eslint" "@typescript-eslint"
], ],
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended" "plugin:@typescript-eslint/recommended-requiring-type-checking"
] ]
} }
] ]

View file

@ -1,7 +1,7 @@
{ {
"name": "asset-transfer-basic", "name": "asset-transfer-events",
"version": "1.0.0", "version": "1.0.0",
"description": "Asset Transfer Basic Application implemented in typeScript using fabric-gateway", "description": "Asset Transfer Events Application implemented in typeScript using fabric-gateway",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
"engines": { "engines": {

View file

@ -7,7 +7,6 @@
import * as grpc from '@grpc/grpc-js'; import * as grpc from '@grpc/grpc-js';
import { ChaincodeEvent, CloseableAsyncIterable, connect, Contract, GatewayError, Network } from '@hyperledger/fabric-gateway'; import { ChaincodeEvent, CloseableAsyncIterable, connect, Contract, GatewayError, Network } from '@hyperledger/fabric-gateway';
import { TextDecoder } from 'util'; import { TextDecoder } from 'util';
import { newGrpcConnection, newIdentity, newSigner } from './connect'; import { newGrpcConnection, newIdentity, newSigner } from './connect';
const channelName = 'mychannel'; const channelName = 'mychannel';
@ -19,9 +18,7 @@ const assetId = `asset${now}`;
async function main(): Promise<void> { async function main(): Promise<void> {
// The gRPC client connection should be shared by all Gateway connections to this endpoint.
const client = await newGrpcConnection(); const client = await newGrpcConnection();
const gateway = connect({ const gateway = connect({
client, client,
identity: await newIdentity(), identity: await newIdentity(),
@ -43,30 +40,19 @@ async function main(): Promise<void> {
let events: CloseableAsyncIterable<ChaincodeEvent> | undefined; let events: CloseableAsyncIterable<ChaincodeEvent> | undefined;
try { try {
// Get a network instance representing the channel where the smart contract is deployed.
const network = gateway.getNetwork(channelName); const network = gateway.getNetwork(channelName);
// Get the smart contract from the network.
const contract = network.getContract(chaincodeName); const contract = network.getContract(chaincodeName);
//Start Listening to events. // Listen for events emitted by subsequent transactions
events = await startEventListening(network); events = await startEventListening(network);
// Create a new asset on the ledger.
const firstBlockNumber = await createAsset(contract); const firstBlockNumber = await createAsset(contract);
await updateAsset(contract);
// Update an existing asset.
await updateAsset(contract)
// Transfer an existing asset.
await transferAsset(contract); await transferAsset(contract);
// Delete asset by assetID.
await deleteAssetByID(contract); await deleteAssetByID(contract);
// Replay all the events received. // Replay events from the block containing the first transaction
await replayChaincodeEvents(network,firstBlockNumber) await replayChaincodeEvents(network,firstBlockNumber);
} finally { } finally {
events?.close(); events?.close();
gateway.close(); gateway.close();
@ -79,106 +65,91 @@ main().catch(error => {
process.exitCode = 1; process.exitCode = 1;
}); });
/**
* Start listening to events.
*/
async function startEventListening(network: Network): Promise<CloseableAsyncIterable<ChaincodeEvent>> { async function startEventListening(network: Network): Promise<CloseableAsyncIterable<ChaincodeEvent>> {
console.log('\n*** Start chaincode event listening\n'); console.log('\n*** Start chaincode event listening');
const events = await network.getChaincodeEvents(chaincodeName); const events = await network.getChaincodeEvents(chaincodeName);
readEvents(events); void readEvents(events); // Don't await - run asynchronously
return events; return events;
} }
/**
* Read events.
*/
async function readEvents(events: CloseableAsyncIterable<ChaincodeEvent>): Promise<void> { async function readEvents(events: CloseableAsyncIterable<ChaincodeEvent>): Promise<void> {
try { try {
for await (const event of events) { for await (const event of events) {
const payload = utf8Decoder.decode(event.payload); const payload = parseJson(event.payload);
console.log(`\n<-- Chaincode event received, name: ${event.eventName}, payload: ${payload}, txID: ${event.transactionId}, blockNumber:${event.blockNumber}`); console.log(`\n<-- Chaincode event received: ${event.eventName} -`, payload);
} }
} catch (error: unknown) { } catch (error: unknown) {
// Ignore the read error when events.close() is called explicitly
if (!(error instanceof GatewayError) || error.code !== grpc.status.CANCELLED) { if (!(error instanceof GatewayError) || error.code !== grpc.status.CANCELLED) {
throw error; throw error;
} }
} }
} }
/** function parseJson(jsonBytes: Uint8Array): unknown {
* Submit a transaction asynchronously to create a new asset. const json = utf8Decoder.decode(jsonBytes);
*/ return JSON.parse(json);
async function createAsset(contract: Contract): Promise<bigint> { }
console.log(`\n --> Submit Transaction: CreateAsset, creates ${assetId} owned by Tom with appraised value 100`);
const result = await contract.submitAsync('CreateAsset', async function createAsset(contract: Contract): Promise<bigint> {
{arguments: [assetId,'yellow','5','Tom','100',]}); console.log(`\n--> Submit Transaction: CreateAsset, ${assetId} owned by Sam with appraised value 100`);
const result = await contract.submitAsync('CreateAsset', {
arguments: [ assetId, 'blue', '10', 'Sam', '100' ],
});
const status = await result.getStatus(); const status = await result.getStatus();
if (!status.successful) { if (!status.successful) {
throw new Error(`failed to commit transaction ${status.transactionId} with status code ${status.code}`); throw new Error(`failed to commit transaction ${status.transactionId} with status code ${status.code}`);
} }
console.log('\n*** CreateAsset committed successfully\n') console.log('\n*** CreateAsset committed successfully');
return status.blockNumber; return status.blockNumber;
} }
/**
* Submit transaction synchronously, to updateAsset the asset appraised value.
*/
async function updateAsset(contract: Contract): Promise<void> { async function updateAsset(contract: Contract): Promise<void> {
console.log(`\n--> Submit transaction: UpdateAsset, ${assetId} update appraised value to 200`); console.log(`\n--> Submit transaction: UpdateAsset, ${assetId} update appraised value to 200`);
await contract.submitTransaction('UpdateAsset', await contract.submitTransaction('UpdateAsset', assetId, 'blue', '10', 'Sam', '200');
assetId,'yellow','5','Tom','200',);
console.log('\n*** UpdateAsset committed successfully\n') console.log('\n*** UpdateAsset committed successfully');
} }
/**
* Submit transaction synchronously, to transfer the asset.
*/
async function transferAsset(contract: Contract): Promise<void> { async function transferAsset(contract: Contract): Promise<void> {
console.log(`\n--> Submit transaction: TransferAsset, ${assetId} to Saptha\n`); console.log(`\n--> Submit transaction: TransferAsset, ${assetId} to Mary`);
await contract.submitTransaction('TransferAsset', await contract.submitTransaction('TransferAsset', assetId, 'Mary');
assetId, 'Saptha',);
console.log('\n*** TransferAsset committed successfully\n') console.log('\n*** TransferAsset committed successfully');
} }
/** async function deleteAssetByID(contract: Contract): Promise<void>{
* Submit a transaction synchronously to delete an asset by ID. console.log(`\n--> Submit transaction: DeleteAsset, ${assetId}`);
*/
async function deleteAssetByID(contract:Contract): Promise<void>{
console.log(`\n--> Submit transaction: DeleteAsset ${assetId}`);
await contract.submitTransaction('DeleteAsset', await contract.submitTransaction('DeleteAsset', assetId);
assetId
);
console.log('\n*** DeleteAsset committed successfully\n') console.log('\n*** DeleteAsset committed successfully');
} }
/** async function replayChaincodeEvents(network: Network, startBlock: bigint): Promise<void>{
* Replay all the events from the start block.
*/
async function replayChaincodeEvents(network:Network,startBlock:bigint):Promise<void>{
const events = await network.getChaincodeEvents(chaincodeName, { const events = await network.getChaincodeEvents(chaincodeName, {
startBlock startBlock,
}); });
try { try {
for await (const event of events) { for await (const event of events) {
const payload = utf8Decoder.decode(event.payload); const payload = parseJson(event.payload);
console.log(`<-- Chaincode event replayed: ${event.eventName}, payload: ${payload}`); console.log(`\n<-- Chaincode event replayed: ${event.eventName} -`, payload);
if(event.eventName === 'DeleteAsset'){
break if (event.eventName === 'DeleteAsset') {
break;
} }
} }
}finally { } finally {
events.close() events.close();
} }
} }