bsp-network
Version:
SDK for writing node.js applications to interact with bsp network. This package encapsulates the APIs to connect to a bsp network, submit transactions and perform queries against the ledger.
143 lines • 5.81 kB
JavaScript
"use strict";
/**
* Copyright 2019 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
const timeouterror_1 = require("../../errors/timeouterror");
const Logger = require("../../logger");
const logger = Logger.getLogger('TransactionEventHandler');
/**
* Handles events for a given transaction. Used to wait for a submitted transaction to be successfully commited to
* the ledger.
* Delegates to an event strategy to decide whether events or errors received should be interpreted as success or
* failure of a transaction.
* @private
*/
class TransactionEventHandler {
/**
* Constructor.
* @private
* @param {Transaction} transaction - Transaction object.
* @param {Object} strategy - Event strategy implementation.
* @param {TransactionOptions} [options] Additional options.
*/
constructor(transactionId, network, strategy) {
this.listener = this.eventCallback.bind(this);
this.strategySuccessCallback = this.strategySuccess.bind(this);
this.strategyFailCallback = this.strategyFail.bind(this);
const method = 'constructor';
this.transactionId = transactionId;
this.network = network;
this.strategy = strategy;
const defaultOptions = {
commitTimeout: 30
};
this.options = Object.assign(defaultOptions, network.getGateway().getOptions().eventHandlerOptions);
logger.debug('%s: transactionId = %s, options = %j', method, this.transactionId, this.options);
this.peers = strategy.getPeers();
this.unrespondedPeers = new Set(this.peers);
this.notificationPromise = new Promise((resolve, reject) => {
this.resolveNotificationPromise = resolve;
this.rejectNotificationPromise = reject;
});
}
/**
* Called to initiate listening for transaction events.
*/
async startListening() {
const method = 'startListening';
if (this.peers && this.peers.length > 0) {
logger.debug('%s - have eventService list - start monitoring', method);
this.setListenTimeout();
await this.network.addCommitListener(this.listener, this.peers, this.transactionId);
}
else {
logger.error('%s - No event services', method);
// shutdown the monitoring
this.resolveNotificationPromise();
}
}
/**
* Wait until enough events have been received from the event services to satisfy the event handling strategy.
* @throws {Error} if the transaction commit is not successful within the timeout period.
*/
async waitForEvents() {
logger.debug('waitForEvents start');
await this.notificationPromise;
logger.debug('waitForEvents end');
}
/**
* Cancel listening for events.
*/
cancelListening() {
logger.debug('cancelListening called');
if (this.timeoutHandler) {
clearTimeout(this.timeoutHandler);
}
this.network.removeCommitListener(this.listener);
}
eventCallback(error, event) {
if (event && !event.isValid) {
const message = `Commit of transaction ${this.transactionId} failed on peer ${event.peer.name} with status ${event.status}`;
this.strategyFail(new Error(message));
}
const peer = (error === null || error === void 0 ? void 0 : error.peer) || event.peer;
if (!this.unrespondedPeers.delete(peer)) {
// Already seen a response from this peer
return;
}
if (error) {
this.strategy.errorReceived(this.strategySuccessCallback, this.strategyFailCallback);
}
else {
this.strategy.eventReceived(this.strategySuccessCallback, this.strategyFailCallback);
}
}
setListenTimeout() {
const method = 'setListenTimeout';
if (typeof this.options.commitTimeout !== 'number' || this.options.commitTimeout <= 0) {
logger.debug('%s - no commit timeout', method);
return;
}
logger.debug('%s setTimeout(%s) in seconds for transaction %s', method, this.options.commitTimeout, this.transactionId);
this.timeoutHandler = setTimeout(() => {
this.timeoutFail();
logger.error('%s - event handler timed out', method);
}, this.options.commitTimeout * 1000);
logger.debug('%s - end', method);
}
timeoutFail() {
const unrespondedPeerNames = Array.from(this.unrespondedPeers)
.map((peer) => peer.name)
.join(', ');
const errorInfo = {
message: 'Event strategy not satisfied within the timeout period. No response received from peers: ' + unrespondedPeerNames,
transactionId: this.transactionId
};
const error = new timeouterror_1.TimeoutError(errorInfo);
this.strategyFail(error);
}
/**
* Callback for the strategy to indicate successful commit of the transaction.
* @private
*/
strategySuccess() {
logger.debug('strategySuccess: commit success for transaction %j', this.transactionId);
this.cancelListening();
this.resolveNotificationPromise();
}
/**
* Callback for the strategy to indicate failure of the transaction commit.
* @private
* @param {Error} error Reason for failure.
*/
strategyFail(error) {
logger.warn('strategyFail: commit failure for transaction %j: %s', this.transactionId, error);
this.cancelListening();
this.rejectNotificationPromise(error);
}
}
exports.TransactionEventHandler = TransactionEventHandler;
//# sourceMappingURL=transactioneventhandler.js.map