Skip to content

Commit

Permalink
FABN-905 Add typescript support for event
Browse files Browse the repository at this point in the history
typescript support for event handler capabilities
in the fabric-network component were not
included. This CR provides this missing implementation

Change-Id: Ib49b8031ee508ee60dae2b4d290c38526003dc8d
Signed-off-by: Dave Kelsey <[email protected]>
  • Loading branch information
Dave Kelsey committed Sep 5, 2018
1 parent ba2508f commit 6de77fe
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 212 deletions.
2 changes: 1 addition & 1 deletion fabric-network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ module.exports.Gateway = require('./lib/gateway');
module.exports.InMemoryWallet = require('./lib/impl/wallet/inmemorywallet');
module.exports.X509WalletMixin = require('./lib/impl/wallet/x509walletmixin');
module.exports.FileSystemWallet = require('./lib/impl/wallet/filesystemwallet');
module.exports.EventStrategies = require('./lib/eventstrategies');
module.exports.DefaultEventHandlerStrategies = require('fabric-network/lib/impl/event/defaulteventhandlerstrategies');
35 changes: 13 additions & 22 deletions fabric-network/lib/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,21 @@ class Contract {

responses.forEach((responseContent) => {
if (responseContent instanceof Error) {

// this is either an error from the sdk, peer response or chaincode response.
// we can distinguish between sdk vs peer/chaincode by the isProposalResponse flag in the future.
// TODO: would be handy to know which peer the response is from and include it here.
const warning = util.format('Response from attempted peer comms was an error: %j', responseContent);
logger.warn('_validatePeerResponses: ' + warning);
invalidResponseMsgs.push(warning);
invalidResponses.push(responseContent);
} else {

// not an error, if it is from a proposal, verify the response
if (!this.channel.verifyProposalResponse(responseContent)) {
// the node-sdk doesn't provide any external utilities from parsing the responseContent.
// there are internal ones which may do what is needed or we would have to decode the
// protobufs ourselves but it should really be the node sdk doing this.
const warning = util.format('Proposal response from peer failed verification: %j', responseContent.response);
logger.warn('_validatePeerResponses: ' + warning);
invalidResponseMsgs.push(warning);
invalidResponses.push(responseContent);
} else if (responseContent.response.status !== 200) {
const warning = util.format('Unexpected response of %j. Payload was: %j', responseContent.response.status, responseContent.response.payload);
logger.warn('_validatePeerResponses: ' + warning);
invalidResponseMsgs.push(warning);
} else {
validResponses.push(responseContent);
}
// anything else is a successful response ie status will be less the 400.
// in the future we can do things like verifyProposalResponse and compareProposalResponseResults
// as part of an extended client side validation strategy but for now don't perform any client
// side checks as the peers will have to do this anyway and it impacts client performance
validResponses.push(responseContent);
}
});

Expand All @@ -75,7 +68,7 @@ class Contract {
throw new Error(msg);
}

return {validResponses, invalidResponses, invalidResponseMsgs};
return {validResponses, invalidResponses};
}

/**
Expand Down Expand Up @@ -106,12 +99,10 @@ class Contract {
const proposalResponses = results[0];
const proposal = results[1];

//TODO: what to do about invalidResponses
// get only the valid responses to submit to the orderer
const {validResponses} = this._validatePeerResponses(proposalResponses);

//TODO: more to do regarding checking the response (see hlfconnection.invokeChaincode)

eventHandler && await eventHandler.startListening();
eventHandler && (await eventHandler.startListening());

// Submit the endorsed transaction to the primary orderers.
const response = await this.channel.sendTransaction({
Expand All @@ -126,7 +117,7 @@ class Contract {
throw new Error(msg);
}

eventHandler && await eventHandler.waitForEvents();
eventHandler && (await eventHandler.waitForEvents());

// return the payload from the invoked chaincode
let result = null;
Expand Down
32 changes: 20 additions & 12 deletions fabric-network/lib/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
const Client = require('fabric-client');

const Network = require('./network');
const EventStrategies = require('./eventstrategies');
const EventStrategies = require('fabric-network/lib/impl/event/defaulteventhandlerstrategies');

const logger = require('./logger').getLogger('Gateway');

Expand Down Expand Up @@ -40,10 +40,12 @@ class Gateway {

// default options
this.options = {
commitTimeout: 300, // 5 minutes
eventStrategy: EventStrategies.MSPID_SCOPE_ALLFORTX,
queryHandler: './impl/query/defaultqueryhandler',
queryHandlerOptions: {
},
eventHandlerOptions: {
commitTimeout: 300, // 5 minutes
strategy: EventStrategies.MSPID_SCOPE_ALLFORTX
}
};
}
Expand All @@ -53,22 +55,28 @@ class Gateway {
* @property {Wallet} wallet The identity wallet implementation for use with this Gateway instance
* @property {string} identity The identity in the wallet for all interactions on this Gateway instance
* @property {string} [clientTlsIdentity] the identity in the wallet to use as the client TLS identity
* @property {number} [commitTimeout = 300] The timout period in seconds to wait for commit notification to complete
* @property {*} [eventStrategy] Event handling strategy to identify successful transaction commits. A null value
* indicates that no event handling is desired.
* @property {DefaultEventHandlerOptions|Object} [eventHandlerOptions] This defines options for the inbuilt default
* event handler capability
*/

/**
* Initialize the Gateway with a connection profile
* @typedef {Object} DefaultEventHanderOptions
* @property {number} [commitTimeout = 300] The timeout period in seconds to wait for commit notification to complete
* @property {*} [strategy] Event handling strategy to identify successful transaction commits. A null value
* indicates that no event handling is desired.
*/

/**
* Connect to the Gateway with a connection profile or a prebuilt Client instance.
*
* @param {Client | string} config The configuration for this Gateway which can come from a common connection
* profile or an existing fabric-client Client instance
* @see see {Client}
* @param {GatewayOptions} options specific options for creating this Gateway instance
* @memberof Gateway
*/
async initialize(config, options) {
const method = 'initialize';
async connect(config, options) {
const method = 'connect';
logger.debug('in %s', method);

if (!options || !options.wallet) {
Expand Down Expand Up @@ -150,12 +158,12 @@ class Gateway {
}

/**
* clean up this Gateway in prep for it to be discarded and garbage collected
* clean up and disconnect this Gateway in prep for it to be discarded and garbage collected
*
* @memberof Gateway
*/
dispose() {
logger.debug('in cleanup');
disconnect() {
logger.debug('in disconnect');
for (const network of this.networks.values()) {
network._dispose();
}
Expand Down
1 change: 1 addition & 0 deletions fabric-network/lib/impl/event/abstracteventstrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class AbstractEventStrategy {
* @param {Function} successFn Callback function to invoke if the strategy is successful.
* @param {Function} failFn Callback function to invoke if the strategy fails.
*/
//eslint-disable-next-line no-unused-vars
checkCompletion(counts, successFn, failFn) {
throw new Error('AbstractEventStrategy.checkCompletion() not implemented');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ function MSPID_SCOPE_ANYFORTX(eventHubFactory, network, mspId) {
return new AnyForTxStrategy(eventHubFactory, peers);
}

//eslint-disable-next-line no-unused-vars
function NETWORK_SCOPE_ALLFORTX(eventHubFactory, network, mspId) {
const peers = network.getChannel().getPeers();
return new AllForTxStrategy(eventHubFactory, peers);
}

//eslint-disable-next-line no-unused-vars
function NETWORK_SCOPE_ANYFORTX(eventHubFactory, network, mspId) {
const peers = network.getChannel().getPeers();
return new AnyForTxStrategy(eventHubFactory, peers);
Expand Down
12 changes: 6 additions & 6 deletions fabric-network/lib/impl/event/eventhubfactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class EventHubFactory {
if (!eventHub.isconnected()) {
await this.connectEventHub(eventHub);
} else {
logger.info('getEventHub:', 'event hub already connected:', eventHub.getName());
eventHub.checkConnection(true);
// event hub is already connected, nothing else needs to be done
logger.debug('getEventHub:', 'event hub already connected:', eventHub.getName());
}
return eventHub;
}
Expand All @@ -67,14 +67,14 @@ class EventHubFactory {
* @param {ChannelEventHub} eventHub An event hub.
*/
async connectEventHub(eventHub) {
const connectPromise = new Promise((resolve, reject) => {
const connectPromise = new Promise((resolve) => {
const regId = eventHub.registerBlockEvent(
(block) => {
logger.info('connectEventHub:', 'successfully connected event hub:', eventHub.getName());
() => {
logger.debug('connectEventHub:', 'successfully connected event hub:', eventHub.getName());
eventHub.unregisterBlockEvent(regId);
resolve();
},
(err) => {
() => {
logger.info('connectEventHub:', 'failed to connect event hub:', eventHub.getName());
eventHub.unregisterBlockEvent(regId);
resolve();
Expand Down
4 changes: 2 additions & 2 deletions fabric-network/lib/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ class Network {
this.eventHandlerFactory = {
createTxEventHandler: () => null
};
const createEventStrategyFn = gateway.getOptions().eventStrategy;
const createEventStrategyFn = gateway.getOptions().eventHandlerOptions.strategy;
if (createEventStrategyFn) {
const self = this;
const eventHubFactory = new EventHubFactory(channel);
const mspId = gateway.getCurrentIdentity()._mspId;
const commitTimeout = gateway.getOptions().commitTimeout;
const commitTimeout = gateway.getOptions().eventHandlerOptions.commitTimeout;
this.eventHandlerFactory.createTxEventHandler = (txId) => {
const eventStrategy = createEventStrategyFn(eventHubFactory, self, mspId);
return new TransactionEventHandler(txId, eventStrategy, { timeout: commitTimeout });
Expand Down
95 changes: 20 additions & 75 deletions fabric-network/test/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,62 +111,34 @@ describe('Contract', () => {
});

it('should return only the valid responses', () => {
const resp1 = {

// simulate an Error from node-sdk itself
const resp1 = new Error('had a problem');

// simulate a good response
const resp2 = {
response: {
status: 200,
payload: 'no error'
}
};

const resp2 = new Error('had a problem');

const resp3 = {
response: {
status: 500,
payload: 'such error'
}
// simulate a bad response from peer, chaincode
const resp3 = new Error('some other error');
resp3.response = {
status: 500,
message: 'some other error'
};

const responses = [resp1, resp2, resp3];

mockChannel.verifyProposalResponse.returns(true);
mockChannel.compareProposalResponseResults.returns(true);

(function() {
const {validResponses} = contract._validatePeerResponses(responses);
validResponses.should.deep.equal([resp1]);
validResponses.should.deep.equal([resp2]);

}).should.not.throw();

});

it('should log warning if verifyProposal returns false', () => {
const response1 = {
response: {
status: 200,
payload: 'NOTVALID'
}
};
const response2 = {
response: {
status: 200,
payload: 'I AM VALID'
}
};

const responses = [ response1, response2 ];

mockChannel.verifyProposalResponse.withArgs(response1).returns(false);
mockChannel.verifyProposalResponse.withArgs(response2).returns(true);
mockChannel.compareProposalResponseResults.returns(true);
(function() {
const {validResponses, invalidResponses, invalidResponseMsgs} = contract._validatePeerResponses(responses);
validResponses.should.deep.equal([response2]);
invalidResponses.should.deep.equal([response1]);
invalidResponseMsgs.length.should.equal(1);
invalidResponseMsgs[0].should.equal('Proposal response from peer failed verification: {"status":200,"payload":"NOTVALID"}');
}).should.not.throw();
});
});

describe('#submitTransaction', () => {
Expand Down Expand Up @@ -293,21 +265,20 @@ describe('Contract', () => {

it('should throw if no valid proposal responses', () => {
const proposalResponses = [
{
response: { status: 500, payload: 'got an error' }
},
Object.assign(new Error(),
{
response: { status: 500, payload: 'got an error' }
}),
new Error('had a problem'),
{
response: { status: 500, payload: 'oh oh another error' }
}
Object.assign(new Error(),
{
response: { status: 500, payload: 'oh oh another error' }
})
];
const proposal = { proposal: 'i do' };
const header = { header: 'gooooal' };
mockChannel.sendTransactionProposal.resolves([ proposalResponses, proposal, header ]);

mockChannel.verifyProposalResponse.returns(true);
mockChannel.compareProposalResponseResults.returns(true);

// Remove the stubbing of _validatePeerResponses in beforeEach()
contract._validatePeerResponses.restore();

Expand Down Expand Up @@ -336,32 +307,6 @@ describe('Contract', () => {
});

describe('#executeTransaction', () => {
/*
beforeEach(() => {
mockChannel.getPeers.returns([mockPeer1]);
mockPeer1.isInRole.withArgs(FABRIC_CONSTANTS.NetworkConfig.EVENT_SOURCE_ROLE).returns(true);
mockChannel.newChannelEventHub.withArgs(mockPeer1).returns(mockEventHub1);
contract._connectToEventHubs();
mockEventHub1.isconnected.returns(true);
mockEventHub1.getPeerAddr.returns('mockPeer1');
});
*/

// it('should throw if functionName not specified', () => {
// return contract.queryChainCode(mockSecurityContext, null, [])
// .should.be.rejectedWith(/functionName not specified/);
// });

// it('should throw if args not specified', () => {
// return contract.queryChainCode(mockSecurityContext, 'myfunc', null)
// .should.be.rejectedWith(/args not specified/);
// });

// it('should throw if args contains non-string values', () => {
// return contract.queryChainCode(mockSecurityContext, 'myfunc', [3.142])
// .should.be.rejectedWith(/invalid arg specified: 3.142/);
// });

it('should query chaincode and handle a good response without return data', async () => {
mockQueryHandler.queryChaincode.withArgs('someid', mockTransactionID, 'myfunc', ['arg1', 'arg2']).resolves();

Expand Down
Loading

0 comments on commit 6de77fe

Please sign in to comment.