From 525d7c6026af34b0c7dba079d37e0c61cfbdb903 Mon Sep 17 00:00:00 2001 From: Sourav Date: Wed, 11 Mar 2026 17:32:42 +0530 Subject: [PATCH] refactor: move batching fallback logic to handler Signed-off-by: Sourav --- libraries/fabric-shim/lib/handler.js | 8 +++++ libraries/fabric-shim/lib/stub.js | 12 +++---- libraries/fabric-shim/test/unit/handler.js | 40 ++++++++++++++++++++++ libraries/fabric-shim/test/unit/stub.js | 30 +++++----------- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/libraries/fabric-shim/lib/handler.js b/libraries/fabric-shim/lib/handler.js index 628ff68a..eac09843 100644 --- a/libraries/fabric-shim/lib/handler.js +++ b/libraries/fabric-shim/lib/handler.js @@ -374,6 +374,14 @@ class ChaincodeMessageHandler { handleMessage(msg, this, 'invoke'); } + async handleGetMultipleStates(keys, channel_id, txid) { + return await Promise.all(keys.map(key => this.handleGetState('', key, channel_id, txid))); + } + + async handleGetMultiplePrivateData(collection, keys, channel_id, txid) { + return await Promise.all(keys.map(key => this.handleGetState(collection, key, channel_id, txid))); + } + async handleGetState(collection, key, channel_id, txId) { const msgPb = new peer.GetState(); msgPb.setKey(key); diff --git a/libraries/fabric-shim/lib/stub.js b/libraries/fabric-shim/lib/stub.js index bcc514b3..54792528 100644 --- a/libraries/fabric-shim/lib/stub.js +++ b/libraries/fabric-shim/lib/stub.js @@ -946,12 +946,11 @@ class ChaincodeStub { */ async getMultipleStates(keys) { logger.debug('getMultipleStates called with keys:%j', keys); - if (!Array.isArray(keys)) { + if (!Array.isArray(keys) || keys.some((key) => typeof key !== 'string')) { throw new Error('keys must be an array of strings'); } - const promises = keys.map(key => this.getState(key)); - return await Promise.all(promises); + return await this.handler.handleGetMultipleStates(keys, this.channel_id, this.txId); } /** @@ -964,15 +963,14 @@ class ChaincodeStub { */ async getMultiplePrivateData(collection, keys) { logger.debug('getMultiplePrivateData called with collection:%s, keys:%j', collection, keys); - if (!collection || typeof collection !== 'string') { + if (typeof collection !== 'string') { throw new Error('collection must be a valid string'); } - if (!Array.isArray(keys)) { + if (!Array.isArray(keys) || keys.some((key) => typeof key !== 'string')) { throw new Error('keys must be an array of strings'); } - const promises = keys.map(key => this.getPrivateData(collection, key)); - return await Promise.all(promises); + return await this.handler.handleGetMultiplePrivateData(collection, keys, this.channel_id, this.txId); } /** diff --git a/libraries/fabric-shim/test/unit/handler.js b/libraries/fabric-shim/test/unit/handler.js index 57d03946..a8c0526c 100644 --- a/libraries/fabric-shim/test/unit/handler.js +++ b/libraries/fabric-shim/test/unit/handler.js @@ -899,6 +899,46 @@ describe('Handler', () => { }); }); + describe('handleGetMultipleStates', () => { + let handleGetStateStub; + afterEach(() => { + sandbox.restore(); + }); + + it('should stub handleGetState and return buffered values', async () => { + const mockStream = {write: sinon.stub(), end: sinon.stub()}; + const handler = new Handler.ChaincodeMessageHandler(mockStream, mockChaincodeImpl); + handleGetStateStub = sandbox.stub(handler, 'handleGetState').resolves(Buffer.from('value')); + + const result = await handler.handleGetMultipleStates(['key1', 'key2'], 'theChannelID', 'theTxID'); + + expect(result).to.deep.equal([Buffer.from('value'), Buffer.from('value')]); + expect(handleGetStateStub.calledTwice).to.be.true; + expect(handleGetStateStub.firstCall.args).to.deep.equal(['', 'key1', 'theChannelID', 'theTxID']); + expect(handleGetStateStub.secondCall.args).to.deep.equal(['', 'key2', 'theChannelID', 'theTxID']); + }); + }); + + describe('handleGetMultiplePrivateData', () => { + let handleGetStateStub; + afterEach(() => { + sandbox.restore(); + }); + + it('should stub handleGetState and return buffered values', async () => { + const mockStream = {write: sinon.stub(), end: sinon.stub()}; + const handler = new Handler.ChaincodeMessageHandler(mockStream, mockChaincodeImpl); + handleGetStateStub = sandbox.stub(handler, 'handleGetState').resolves(Buffer.from('value')); + + const result = await handler.handleGetMultiplePrivateData('collection1', ['key1', 'key2'], 'theChannelID', 'theTxID'); + + expect(result).to.deep.equal([Buffer.from('value'), Buffer.from('value')]); + expect(handleGetStateStub.calledTwice).to.be.true; + expect(handleGetStateStub.firstCall.args).to.deep.equal(['collection1', 'key1', 'theChannelID', 'theTxID']); + expect(handleGetStateStub.secondCall.args).to.deep.equal(['collection1', 'key2', 'theChannelID', 'theTxID']); + }); + }); + describe('handleGetState', () => { const key = 'theKey'; const collection = ''; diff --git a/libraries/fabric-shim/test/unit/stub.js b/libraries/fabric-shim/test/unit/stub.js index 15429051..0ecda526 100644 --- a/libraries/fabric-shim/test/unit/stub.js +++ b/libraries/fabric-shim/test/unit/stub.js @@ -1108,7 +1108,7 @@ describe('Stub', () => { beforeEach(() => { stub = new Stub({ - handleGetState: sinon.stub().resolves(Buffer.from('value')) + handleGetMultipleStates: sinon.stub().resolves([Buffer.from('value1'), Buffer.from('value2')]) }, 'dummyChannelId', 'dummyTxid', chaincodeInput); }); @@ -1116,18 +1116,11 @@ describe('Stub', () => { await expect(stub.getMultipleStates('not-an-array')).to.be.rejectedWith(/keys must be an array of strings/); }); - it('should call getState for each key and return the results', async () => { - sandbox.stub(stub, 'getState').callsFake(async (key) => { - if (key === 'key1') { - return Buffer.from('value1'); - } else if (key === 'key2') { - return Buffer.from('value2'); - } - }); - + it('should call call handleGetMultipleStates on the handler and return the results', async () => { const results = await stub.getMultipleStates(['key1', 'key2']); expect(results).to.deep.equal([Buffer.from('value1'), Buffer.from('value2')]); - sinon.assert.calledTwice(stub.getState); + sinon.assert.calledOnce(stub.handler.handleGetMultipleStates); + sinon.assert.calledWith(stub.handler.handleGetMultipleStates, ['key1', 'key2'], 'dummyChannelId', 'dummyTxid'); }); }); @@ -1136,7 +1129,7 @@ describe('Stub', () => { beforeEach(() => { stub = new Stub({ - handleGetState: sinon.stub().resolves(Buffer.from('value')) + handleGetMultiplePrivateData: sinon.stub().resolves([Buffer.from('value1'), Buffer.from('value2')]) }, 'dummyChannelId', 'dummyTxid', chaincodeInput); }); @@ -1148,18 +1141,11 @@ describe('Stub', () => { await expect(stub.getMultiplePrivateData('collection', 'not-an-array')).to.be.rejectedWith(/keys must be an array of strings/); }); - it('should call getPrivateData for each key and return the results', async () => { - sandbox.stub(stub, 'getPrivateData').callsFake(async (collection, key) => { - if (key === 'key1') { - return Buffer.from('value1'); - } else if (key === 'key2') { - return Buffer.from('value2'); - } - }); - + it('should call handleGetMultiplePrivateData on the handler and return the results', async () => { const results = await stub.getMultiplePrivateData('collection', ['key1', 'key2']); expect(results).to.deep.equal([Buffer.from('value1'), Buffer.from('value2')]); - sinon.assert.calledTwice(stub.getPrivateData); + sinon.assert.calledOnce(stub.handler.handleGetMultiplePrivateData); + sinon.assert.calledWith(stub.handler.handleGetMultiplePrivateData, 'collection', ['key1', 'key2'], 'dummyChannelId', 'dummyTxid'); }); });