Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit fb37909

Browse files
author
Dave Kelsey
authored
[0.19.x] Allow txid to be passed to query requests (#4499)
Signed-off-by: Dave Kelsey <d_kelsey@uk.ibm.com>
1 parent e964cf3 commit fb37909

File tree

2 files changed

+171
-15
lines changed

2 files changed

+171
-15
lines changed

packages/composer-connector-hlfv1/lib/hlfqueryhandler.js

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class HLFQueryHandler {
105105
}
106106

107107
if (!success) {
108-
const newError = new Error(`No peers available to query. last error was ${allErrors[allErrors.length-1]}`);
108+
const newError = new Error(`No peers available to query. last error was ${allErrors[allErrors.length - 1]}`);
109109
LOG.error(method, newError);
110110
throw newError;
111111
}
@@ -140,8 +140,8 @@ class HLFQueryHandler {
140140
};
141141

142142
const t0 = Date.now();
143-
let payloads = await this.connection.channel.queryByChaincode(request);
144-
LOG.perf(method, `Total duration for node-sdk queryByChaincode to ${functionName}: `, txId, t0);
143+
let payloads = await this.queryByChaincode(request);
144+
LOG.perf(method, `Total duration for queryByChaincode to ${functionName}: `, txId, t0);
145145
LOG.debug(method, `Received ${payloads.length} payloads(s) from querying the composer runtime chaincode`);
146146
if (!payloads.length) {
147147
LOG.error(method, 'No payloads were returned from the query request:' + functionName);
@@ -159,6 +159,43 @@ class HLFQueryHandler {
159159
return payload;
160160

161161
}
162+
163+
/**
164+
* Perform a chaincode query and parse the responses.
165+
* @param {object} request the proposal for a query
166+
* @return {array} the responses
167+
*/
168+
async queryByChaincode(request) {
169+
const method = 'queryByChaincode';
170+
LOG.entry(method, request);
171+
try {
172+
const results = await this.connection.channel.sendTransactionProposal(request);
173+
const responses = results[0];
174+
if (responses && Array.isArray(responses)) {
175+
let results = [];
176+
for (let i = 0; i < responses.length; i++) {
177+
let response = responses[i];
178+
if (response instanceof Error) {
179+
results.push(response);
180+
}
181+
else if (response.response && response.response.payload) {
182+
results.push(response.response.payload);
183+
}
184+
else {
185+
results.push(new Error(response));
186+
}
187+
}
188+
LOG.exit(method);
189+
return results;
190+
}
191+
const err = new Error('Payload results are missing from the chaincode query');
192+
LOG.error(method, err);
193+
throw err;
194+
} catch(err) {
195+
LOG.error(method, err);
196+
throw err;
197+
}
198+
}
162199
}
163200

164201
module.exports = HLFQueryHandler;

packages/composer-connector-hlfv1/test/hlfqueryhandler.js

Lines changed: 131 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('HLFQueryHandler', () => {
7373

7474
it('should not switch to another peer if peer returns a payload which is an error', async () => {
7575
const response = new Error('my chaincode error');
76-
mockChannel.queryByChaincode.resolves([response]);
76+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
7777
let qspSpy = sinon.spy(queryHandler, 'querySinglePeer');
7878
try {
7979
await queryHandler.queryChaincode(mockTransactionID, 'myfunc', ['arg1', 'arg2']);
@@ -175,11 +175,11 @@ describe('HLFQueryHandler', () => {
175175

176176
it('should query a single peer', async () => {
177177
const response = Buffer.from('hello world');
178-
mockChannel.queryByChaincode.resolves([response]);
178+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
179179
mockConnection.businessNetworkIdentifier = 'org-acme-biznet';
180180
let result = await queryHandler.querySinglePeer(mockPeer2, mockTransactionID, 'myfunc', ['arg1', 'arg2']);
181-
sinon.assert.calledOnce(mockChannel.queryByChaincode);
182-
sinon.assert.calledWith(mockChannel.queryByChaincode, {
181+
sinon.assert.calledOnce(queryHandler.queryByChaincode);
182+
sinon.assert.calledWith(queryHandler.queryByChaincode, {
183183
chaincodeId: 'org-acme-biznet',
184184
txId: mockTransactionID,
185185
fcn: 'myfunc',
@@ -191,18 +191,18 @@ describe('HLFQueryHandler', () => {
191191
});
192192

193193
it('should throw if no responses are returned', () => {
194-
mockChannel.queryByChaincode.resolves([]);
194+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([]);
195195
return queryHandler.querySinglePeer(mockPeer2, 'txid', 'myfunc', ['arg1', 'arg2'])
196196
.should.be.rejectedWith(/No payloads were returned from the query request/);
197197
});
198198

199199
it('should return any responses that are errors and not UNAVAILABLE', async () => {
200200
const response = new Error('such error');
201-
mockChannel.queryByChaincode.resolves([response]);
201+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
202202
mockConnection.businessNetworkIdentifier = 'org-acme-biznet';
203203
let result = await queryHandler.querySinglePeer(mockPeer2, mockTransactionID, 'myfunc', ['arg1', 'arg2']);
204-
sinon.assert.calledOnce(mockChannel.queryByChaincode);
205-
sinon.assert.calledWith(mockChannel.queryByChaincode, {
204+
sinon.assert.calledOnce(queryHandler.queryByChaincode);
205+
sinon.assert.calledWith(queryHandler.queryByChaincode, {
206206
chaincodeId: 'org-acme-biznet',
207207
txId: mockTransactionID,
208208
fcn: 'myfunc',
@@ -216,7 +216,7 @@ describe('HLFQueryHandler', () => {
216216
it('should throw any responses that are errors and code 14 being unavailable.', () => {
217217
const response = new Error('14 UNAVAILABLE: Connect Failed');
218218
response.code = 14;
219-
mockChannel.queryByChaincode.resolves([response]);
219+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
220220
mockConnection.businessNetworkIdentifier = 'org-acme-biznet';
221221
return queryHandler.querySinglePeer(mockPeer2, 'txid', 'myfunc', ['arg1', 'arg2'])
222222
.should.be.rejectedWith(/Connect Failed/);
@@ -225,7 +225,7 @@ describe('HLFQueryHandler', () => {
225225
it('should throw any responses that are errors and code 1 being unavailable.', () => {
226226
const response = new Error('1 UNAVAILABLE: Connect Failed');
227227
response.code = 1;
228-
mockChannel.queryByChaincode.resolves([response]);
228+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
229229
mockConnection.businessNetworkIdentifier = 'org-acme-biznet';
230230
return queryHandler.querySinglePeer(mockPeer2, 'txid', 'myfunc', ['arg1', 'arg2'])
231231
.should.be.rejectedWith(/Connect Failed/);
@@ -234,17 +234,136 @@ describe('HLFQueryHandler', () => {
234234
it('should throw any responses that are errors and code 4 being unavailable.', () => {
235235
const response = new Error('4 UNAVAILABLE: Connect Failed');
236236
response.code = 4;
237-
mockChannel.queryByChaincode.resolves([response]);
237+
sandbox.stub(queryHandler, 'queryByChaincode').resolves([response]);
238238
mockConnection.businessNetworkIdentifier = 'org-acme-biznet';
239239
return queryHandler.querySinglePeer(mockPeer2, 'txid', 'myfunc', ['arg1', 'arg2'])
240240
.should.be.rejectedWith(/Connect Failed/);
241241
});
242242

243243
it('should throw if query request fails', () => {
244-
mockChannel.queryByChaincode.rejects(new Error('Query Failed'));
244+
sandbox.stub(queryHandler, 'queryByChaincode').rejects(new Error('Query Failed'));
245245
return queryHandler.querySinglePeer(mockPeer2, 'txid', 'myfunc', ['arg1', 'arg2'])
246246
.should.be.rejectedWith(/Query Failed/);
247247
});
248248
});
249249

250+
describe('#queryByChaincode', () => {
251+
it('should handle single good response', async () => {
252+
const request = {
253+
id: 1
254+
};
255+
const results = [
256+
[{
257+
response: {
258+
payload: 'some payload'
259+
}
260+
}]
261+
];
262+
mockChannel.sendTransactionProposal.resolves(results);
263+
const responses = await queryHandler.queryByChaincode(request);
264+
sinon.assert.calledOnce(mockChannel.sendTransactionProposal);
265+
sinon.assert.calledWith(mockChannel.sendTransactionProposal, request);
266+
responses.length.should.equal(1);
267+
responses[0].should.equal('some payload');
268+
});
269+
270+
it('should handle multiple good responses', async () => {
271+
const request = {
272+
id: 1
273+
};
274+
const results = [[
275+
{
276+
response: {
277+
payload: 'some payload'
278+
}
279+
},
280+
{
281+
response: {
282+
payload: 'another payload'
283+
}
284+
},
285+
{
286+
response: {
287+
payload: 'final payload'
288+
}
289+
}
290+
]];
291+
mockChannel.sendTransactionProposal.resolves(results);
292+
const responses = await queryHandler.queryByChaincode(request);
293+
sinon.assert.calledOnce(mockChannel.sendTransactionProposal);
294+
sinon.assert.calledWith(mockChannel.sendTransactionProposal, request);
295+
responses.length.should.equal(3);
296+
responses[0].should.equal('some payload');
297+
responses[1].should.equal('another payload');
298+
responses[2].should.equal('final payload');
299+
});
300+
301+
it('should handle single error response', async () => {
302+
const request = {
303+
id: 1
304+
};
305+
const results = [
306+
[ new Error('some error') ]
307+
];
308+
mockChannel.sendTransactionProposal.resolves(results);
309+
const responses = await queryHandler.queryByChaincode(request);
310+
sinon.assert.calledOnce(mockChannel.sendTransactionProposal);
311+
sinon.assert.calledWith(mockChannel.sendTransactionProposal, request);
312+
responses.length.should.equal(1);
313+
responses[0].should.be.instanceOf(Error);
314+
responses[0].message.should.equal('some error');
315+
});
316+
317+
it('should handle multiple different response types', async () => {
318+
const request = {
319+
id: 1
320+
};
321+
const results = [[
322+
323+
new Error('some error'),
324+
325+
{
326+
response: {
327+
payload: 'another payload'
328+
}
329+
},
330+
{
331+
response: 'a strange error'
332+
},
333+
{
334+
data: 'I am not just an android'
335+
}
336+
]];
337+
mockChannel.sendTransactionProposal.resolves(results);
338+
const responses = await queryHandler.queryByChaincode(request);
339+
sinon.assert.calledOnce(mockChannel.sendTransactionProposal);
340+
sinon.assert.calledWith(mockChannel.sendTransactionProposal, request);
341+
responses.length.should.equal(4);
342+
responses[0].should.be.instanceOf(Error);
343+
responses[0].message.should.equal('some error');
344+
responses[1].should.equal('another payload');
345+
responses[2].should.be.instanceOf(Error);
346+
responses[3].should.be.instanceOf(Error);
347+
});
348+
349+
it('should handle no responses', async () => {
350+
const request = {
351+
id: 1
352+
};
353+
let results = [];
354+
mockChannel.sendTransactionProposal.resolves(results);
355+
await queryHandler.queryByChaincode(request).should.be.rejectedWith(/Payload results are missing/);
356+
results = ['not an array'];
357+
mockChannel.sendTransactionProposal.resolves(results);
358+
await queryHandler.queryByChaincode(request).should.be.rejectedWith(/Payload results are missing/);
359+
});
360+
361+
it('should handle error from sendTransactionProposal', async () => {
362+
const request = {
363+
id: 1
364+
};
365+
mockChannel.sendTransactionProposal.rejects(new Error('sendTxProp error'));
366+
await queryHandler.queryByChaincode(request).should.be.rejectedWith(/sendTxProp error/);
367+
});
368+
});
250369
});

0 commit comments

Comments
 (0)