fabric-network
Version:
SDK for writing node.js applications to interact with Hyperledger Fabric. This package encapsulates the APIs to connect to a Fabric network, submit transactions and perform queries against the ledger.
168 lines • 6.06 kB
JavaScript
"use strict";
/*
* Copyright 2020 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlockEventSource = void 0;
const Logger = require("../../logger");
const GatewayUtils = require("../gatewayutils");
const asyncnotifier_1 = require("./asyncnotifier");
const filteredblockeventfactory_1 = require("./filteredblockeventfactory");
const fullblockeventfactory_1 = require("./fullblockeventfactory");
const orderedblockqueue_1 = require("./orderedblockqueue");
const privateblockeventfactory_1 = require("./privateblockeventfactory");
const gatewayutils_1 = require("../gatewayutils");
const Long = require("long");
const logger = Logger.getLogger('BlockEventSource');
const defaultBlockType = 'filtered';
function newBlockQueue(options) {
const startBlock = asLong(options.startBlock);
return new orderedblockqueue_1.OrderedBlockQueue(startBlock);
}
function asLong(value) {
if (gatewayutils_1.notNullish(value)) {
return Long.fromValue(value);
}
return undefined;
}
class BlockEventSource {
constructor(eventServiceManager, options = {}) {
this.listeners = new Set();
this.state = 'ready';
this.eventServiceManager = eventServiceManager;
this.blockQueue = newBlockQueue(options);
this.asyncNotifier = new asyncnotifier_1.AsyncNotifier(this.blockQueue.getNextBlock.bind(this.blockQueue), this.notifyListeners.bind(this));
this.blockType = options.type || defaultBlockType;
logger.debug('constructor - blockType:%s', this.blockType);
}
async addBlockListener(listener) {
this.listeners.add(listener);
await this.start();
return listener;
}
removeBlockListener(listener) {
this.listeners.delete(listener);
}
setState(state) {
if (this.state !== 'stopped') {
this.state = state;
}
}
close() {
this.setState('stopped');
logger.debug('state set to - :%s', this.state);
this._close();
}
_close() {
var _a;
this.unregisterListener();
(_a = this.eventService) === null || _a === void 0 ? void 0 : _a.close();
this.setState('ready');
logger.debug('state set to - :%s', this.state);
if (this.restart) {
clearImmediate(this.restart);
}
}
async start() {
logger.debug('state - :%s', this.state);
if (this.state !== 'ready') {
return;
}
this.state = 'started';
try {
this.eventService = this.eventServiceManager.newDefaultEventService();
this.registerListener(); // Register before start so no events are missed
logger.debug('start - calling startEventService');
await this.startEventService();
}
catch (error) {
logger.error('Failed to start event service', error);
this._close();
this.restart = setImmediate(() => {
void this.start();
});
}
}
registerListener() {
const callback = this.blockEventCallback.bind(this);
const options = {
startBlock: this.getNextBlockNumber(),
unregister: false
};
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.eventListener = this.eventService.registerBlockListener(callback, options);
}
unregisterListener() {
var _a;
try {
(_a = this.eventListener) === null || _a === void 0 ? void 0 : _a.unregisterEventListener();
}
catch (error) {
logger.warn('Failed to unregister listener', error);
}
}
async startEventService() {
let startBlock = this.getNextBlockNumber();
if (startBlock) {
startBlock = startBlock.subtract(Long.ONE);
if (startBlock.isNegative()) {
startBlock = Long.ZERO;
}
}
const options = {
blockType: this.blockType,
startBlock
};
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.eventServiceManager.startEventService(this.eventService, options);
}
blockEventCallback(error, event) {
if (error) {
this._close();
this.restart = setImmediate(() => {
void this.start();
}); // Must schedule after current event loop to avoid recursion in event service notification
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.onBlockEvent(event);
}
}
onBlockEvent(eventInfo) {
const blockEvent = this.newBlockEvent(eventInfo);
this.blockQueue.addBlock(blockEvent);
if (this.blockQueue.size() > 0) {
this.asyncNotifier.notify();
}
}
newBlockEvent(eventInfo) {
if (this.blockType === 'filtered') {
return filteredblockeventfactory_1.newFilteredBlockEvent(eventInfo);
}
else if (this.blockType === 'full') {
return fullblockeventfactory_1.newFullBlockEvent(eventInfo);
}
else if (this.blockType === 'private') {
return privateblockeventfactory_1.newPrivateBlockEvent(eventInfo);
}
else {
throw new Error(`Unsupported event type: ${this.blockType}`);
}
}
async notifyListeners(event) {
const promises = Array.from(this.listeners).map((listener) => listener(event));
const results = await GatewayUtils.allSettled(promises);
for (const result of results) {
if (result.status === 'rejected') {
logger.warn('Error notifying listener', result.reason);
}
}
}
getNextBlockNumber() {
return this.blockQueue.getNextBlockNumber();
}
}
exports.BlockEventSource = BlockEventSource;
//# sourceMappingURL=blockeventsource.js.map