mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
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:
parent
975c9773bd
commit
a492d7d241
3 changed files with 48 additions and 76 deletions
|
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue