Skip to content

Commit 9d45ba7

Browse files
authored
[FABCN-397] State queries limited to 100 results (#141) (#142)
Errow was the new version of protobufjs used to create the bundle.js gave a different property to the 'has more' field. hasMore replaced has_more As this accessed as JS property it didn't fail, but meant only a limited number could be accessed. corrected, added test for 229 results. bit of dead code pruning Signed-off-by: Matthew B White <whitemat@uk.ibm.com>
1 parent 298687a commit 9d45ba7

4 files changed

Lines changed: 39 additions & 49 deletions

File tree

libraries/fabric-shim/lib/iterators.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class CommonIterator {
2020

2121
/**
2222
* constructor
23+
*
24+
* Note that the decoded payload will be a protobuf of type
25+
* fabprotos.protos.QueryResponse
26+
*
2327
* @param {ChaincodeSupportClient} handler client handler
2428
* @param {string} channel_id channel id
2529
* @param {string} txID transaction id
@@ -66,9 +70,8 @@ class CommonIterator {
6670
const queryResult = {};
6771
queryResult.value = this._getResultFromBytes(this.response.results[this.currentLoc]);
6872
this.currentLoc++;
69-
// TODO: potential breaking change if it's assumed that if done == true then it has a valid value
73+
7074
queryResult.done = false;
71-
// queryResult.done = !(this.currentLoc < this.response.results.length || this.response.has_more);
7275
return queryResult;
7376
}
7477

@@ -85,7 +88,7 @@ class CommonIterator {
8588
return this._createAndEmitResult();
8689
} else {
8790
// check to see if there is more and go fetch it
88-
if (this.response.has_more) {
91+
if (this.response.hasMore) {
8992
try {
9093
const response = await this.handler.handleQueryStateNext(this.response.id, this.channel_id, this.txID);
9194
this.currentLoc = 0;

libraries/fabric-shim/test/unit/iterators.js

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ describe('Iterator', () => {
107107
getResultFromBytesStub.restore();
108108
});
109109

110-
it ('should return value of first element of results converted from bytes and done false when has_more false and results has no more elements after currentLoc', () => {
110+
it ('should return value of first element of results converted from bytes and done false when hasMore false and results has no more elements after currentLoc', () => {
111111
mockResponse.results = ['some result bytes'];
112-
mockResponse.has_more = false;
112+
mockResponse.hasMore = false;
113113

114114
const result = ci._createAndEmitResult();
115115

@@ -121,9 +121,9 @@ describe('Iterator', () => {
121121
});
122122
});
123123

124-
it ('should return value of first element of results converted from bytes and done false when has_more true and results has no more elements after currentLoc', () => {
124+
it ('should return value of first element of results converted from bytes and done false when hasMore true and results has no more elements after currentLoc', () => {
125125
mockResponse.results = ['some result bytes'];
126-
mockResponse.has_more = true;
126+
mockResponse.hasMore = true;
127127

128128
const result = ci._createAndEmitResult();
129129

@@ -136,9 +136,9 @@ describe('Iterator', () => {
136136
});
137137
});
138138

139-
it ('should return value of first element of results converted from bytes and done false when has_more false and results has elements after currentLoc', () => {
139+
it ('should return value of first element of results converted from bytes and done false when hasMore false and results has elements after currentLoc', () => {
140140
mockResponse.results = ['some result bytes', 'some more result bytes'];
141-
mockResponse.has_more = false;
141+
mockResponse.hasMore = false;
142142

143143
const result = ci._createAndEmitResult();
144144

@@ -151,9 +151,9 @@ describe('Iterator', () => {
151151
});
152152
});
153153

154-
it ('should return value of first element of results converted from bytes and done false when has_more true and results has elements after currentLoc', () => {
154+
it ('should return value of first element of results converted from bytes and done false when hasMore true and results has elements after currentLoc', () => {
155155
mockResponse.results = ['some result bytes', 'some more result bytes'];
156-
mockResponse.has_more = true;
156+
mockResponse.hasMore = true;
157157

158158
const result = ci._createAndEmitResult();
159159

@@ -168,7 +168,7 @@ describe('Iterator', () => {
168168

169169
it ('should return as expected with non-zero currentLoc', () => {
170170
mockResponse.results = ['some result bytes', 'some more result bytes'];
171-
mockResponse.has_more = true;
171+
mockResponse.hasMore = true;
172172

173173
ci.currentLoc = 1;
174174

@@ -185,7 +185,7 @@ describe('Iterator', () => {
185185

186186
it ('should return value of first element of results converted from bytes and done false', () => {
187187
mockResponse.results = ['some result bytes', 'some more result bytes'];
188-
mockResponse.has_more = false;
188+
mockResponse.hasMore = false;
189189

190190
const expectedResult = {
191191
value: 'some result',
@@ -222,13 +222,13 @@ describe('Iterator', () => {
222222
expect(result).to.deep.equal('some result');
223223
});
224224

225-
it ('should return _createAndEmitResult when response has_more and no error occurs', async () => {
225+
it ('should return _createAndEmitResult when response hasMore and no error occurs', async () => {
226226
mockResponse.results = [];
227-
mockResponse.has_more = true;
227+
mockResponse.hasMore = true;
228228

229229
const nextResponse = {
230230
results: ['some result bytes', 'some more result bytes'],
231-
has_more: false
231+
hasMore: false
232232
};
233233

234234
mockHandler.handleQueryStateNext = sinon.stub().resolves(nextResponse);
@@ -242,36 +242,9 @@ describe('Iterator', () => {
242242
expect(ci.response).to.deep.equal(nextResponse);
243243
});
244244

245-
/*
246-
it ('should emit an error if error occurs when has_more and listenerCount for data > 0', async () => {
245+
it ('should throw an error if error occurs when hasMore and listenerCount for data = 0', async () => {
247246
mockResponse.results = [];
248-
mockResponse.has_more = true;
249-
250-
const err = new Error('some error');
251-
252-
mockHandler.handleQueryStateNext = sinon.stub().rejects(err);
253-
const emitStub = sinon.stub(ci, 'emit');
254-
const listenerCountStub = sinon.stub(ci, 'listenerCount').returns(1);
255-
256-
ci.currentLoc = 1;
257-
258-
const result = await ci.next();
259-
260-
expect(result).to.be.undefined;
261-
expect(createAndEmitResultStub.notCalled).to.be.ok;
262-
expect(listenerCountStub.calledOnce).to.be.ok;
263-
expect(listenerCountStub.firstCall.args).to.deep.equal(['data']);
264-
expect(emitStub.calledOnce).to.be.ok;
265-
expect(emitStub.firstCall.args).to.deep.equal(['error', ci, err]);
266-
267-
listenerCountStub.restore();
268-
emitStub.restore();
269-
});
270-
*/
271-
272-
it ('should throw an error if error occurs when has_more and listenerCount for data = 0', async () => {
273-
mockResponse.results = [];
274-
mockResponse.has_more = true;
247+
mockResponse.hasMore = true;
275248

276249
const err = new Error('some error');
277250

@@ -285,19 +258,19 @@ describe('Iterator', () => {
285258
expect(createAndEmitResultStub.notCalled).to.be.true;
286259
});
287260

288-
it ('should return done if response does not has_more and listenerCount for end > 0', async () => {
261+
it ('should return done if response does not hasMore and listenerCount for end > 0', async () => {
289262
mockResponse.results = [];
290-
mockResponse.has_more = false;
263+
mockResponse.hasMore = false;
291264

292265
const result = await ci.next();
293266

294267
expect(result).to.deep.equal({done: true});
295268
expect(createAndEmitResultStub.notCalled).to.be.true;
296269
});
297270

298-
it ('should return done if response does not has_more and listenerCount for end = 0', async () => {
271+
it ('should return done if response does not hasMore and listenerCount for end = 0', async () => {
299272
mockResponse.results = [];
300-
mockResponse.has_more = false;
273+
mockResponse.hasMore = false;
301274

302275
const result = await ci.next();
303276

test/chaincodes/crud/chaincode.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ class CrudChaincode extends Contract {
6363
await stub.putState(`key${i}`, Buffer.from(`value${i}`));
6464
await stub.putState(`jsonkey${i}`, Buffer.from(JSON.stringify({key: `k${i}`, value: `value${i}`})));
6565
}
66+
67+
// add a large set of keys for testing pagination and larger data sets
68+
const DATA_SET_SIZE=229;
69+
for (let i = 0; i < DATA_SET_SIZE;i++){
70+
const compositeKey = stub.createCompositeKey('bulk-data',['bulk',i.toString().padStart(3,'0')]);
71+
await stub.putState(compositeKey, Buffer.from(i.toString().padStart(3,'0')));
72+
}
6673
}
6774

6875
async getKey({stub}) {

test/fv/crud.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ describe('Chaincode CRUD', () => {
154154
expect(JSON.parse(payload)).to.deep.equal(['annblack', 'annred', 'annyellow']);
155155
});
156156

157+
158+
it('should return the bulk states from a partial composite key using the old iterator style', async function() {
159+
this.timeout(MED_STEP);
160+
const payload = await utils.query(suite, 'org.mynamespace.crud:getStateByPartialCompositeKey', ['bulk-data','bulk']);
161+
expect(JSON.parse(payload)).to.have.lengthOf(229);
162+
});
163+
157164
it('should return a state from a partial composite key using the new iterator style', async function() {
158165
this.timeout(SHORT_STEP);
159166
const payload = await utils.query(suite, 'org.mynamespace.crud:getStateByPartialCompositeKeyUsingAsyncIterator', ['name~color', 'ann']);

0 commit comments

Comments
 (0)