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

Commit 9bd693a

Browse files
authored
Experimental ability to disable historian (#4571)
Signed-off-by: Dave Kelsey <d_kelsey@uk.ibm.com>
1 parent 5a68c3d commit 9bd693a

File tree

9 files changed

+555
-21
lines changed

9 files changed

+555
-21
lines changed

packages/composer-connector-embedded/test/embeddedconnection.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
'use strict';
1616

17-
const { BusinessNetworkDefinition, Certificate, Connection, ConnectionManager } = require('composer-common');
17+
const { BusinessNetworkDefinition, BusinessNetworkMetadata, Certificate, Connection, ConnectionManager } = require('composer-common');
1818
const { Context, DataCollection, DataService, Engine, LoggingService, InstalledBusinessNetwork } = require('composer-runtime');
1919
const EmbeddedContainer = require('composer-runtime-embedded').EmbeddedContainer;
2020
const EmbeddedConnection = require('../lib/embeddedconnection');
@@ -260,6 +260,11 @@ describe('EmbeddedConnection', () => {
260260

261261
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
262262
sandbox.stub(InstalledBusinessNetwork, 'newInstance').resolves(mockInstalledBusinessNetwork);
263+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
264+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
265+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
266+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
267+
mockBusinessNetworkMetadata.getPackageJson.returns({});
263268

264269
// put something weird for installedBusinessNetwork so we can detect it has changed.
265270
EmbeddedConnection.addChaincode('6eeb8858-eced-4a32-b1cd-2491f1e3718f', mockContainer, mockEngine, 'orgInstalledBusinessNetwork');

packages/composer-runtime-embedded/test/embeddedcontext.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
const Serializer = require('composer-common').Serializer;
1818
const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefinition;
19+
const BusinessNetworkMetadata = require('composer-common').BusinessNetworkMetadata;
20+
const InstalledBusinessNetwork = require('composer-runtime').InstalledBusinessNetwork;
1921
const Context = require('composer-runtime').Context;
2022
const Engine = require('composer-runtime').Engine;
2123
const EmbeddedContainer = require('..').EmbeddedContainer;
@@ -43,17 +45,22 @@ describe('EmbeddedContext', () => {
4345
let mockSerializer;
4446
let mockEngine;
4547
let mockEventSink;
46-
let mockInstalledBusinessNetwork;
4748
let context;
4849

49-
beforeEach(() => {
50+
beforeEach(async () => {
5051
mockEmbeddedContainer = sinon.createStubInstance(EmbeddedContainer);
5152
mockEmbeddedContainer.getUUID.returns('d8f08eba-2746-4801-8318-3a7611aed45e');
5253
mockEngine = sinon.createStubInstance(Engine);
5354
mockEngine.getContainer.returns(mockEmbeddedContainer);
5455
mockSerializer = sinon.createStubInstance(Serializer);
5556
mockEventSink = {};
56-
mockInstalledBusinessNetwork = sinon.createStubInstance(BusinessNetworkDefinition);
57+
58+
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
59+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
60+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
61+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
62+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
63+
mockBusinessNetworkMetadata.getPackageJson.returns({});
5764
context = new EmbeddedContext(mockEngine, identity, mockEventSink, mockInstalledBusinessNetwork);
5865
});
5966

packages/composer-runtime-web/test/webcontext.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
const Context = require('composer-runtime').Context;
1818
const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefinition;
19+
const BusinessNetworkMetadata = require('composer-common').BusinessNetworkMetadata;
20+
const InstalledBusinessNetwork = require('composer-runtime').InstalledBusinessNetwork;
1921
const Engine = require('composer-runtime').Engine;
2022
const Serializer = require('composer-common').Serializer;
2123
const WebContainer = require('..').WebContainer;
@@ -42,15 +44,20 @@ describe('WebContext', () => {
4244
let mockSerializer;
4345
let mockEngine;
4446
let context;
45-
let mockInstalledBusinessNetwork;
4647

4748
beforeEach(() => {
4849
mockWebContainer = sinon.createStubInstance(WebContainer);
4950
mockWebContainer.getName.returns('d8f08eba-2746-4801-8318-3a7611aed45e');
5051
mockEngine = sinon.createStubInstance(Engine);
5152
mockEngine.getContainer.returns(mockWebContainer);
5253
mockSerializer = sinon.createStubInstance(Serializer);
53-
mockInstalledBusinessNetwork = sinon.createStubInstance(BusinessNetworkDefinition);
54+
55+
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
56+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
57+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
58+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
59+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
60+
mockBusinessNetworkMetadata.getPackageJson.returns({});
5461
context = new WebContext(mockEngine, mockInstalledBusinessNetwork, identity);
5562
});
5663

packages/composer-runtime/lib/context.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Context {
4141
* @param {InstalledBusinessNetwork} installedBusinessNetwork Information associated with the installed business network
4242
*/
4343
constructor(engine, installedBusinessNetwork) {
44+
const method = 'constructor';
4445
if (!installedBusinessNetwork) {
4546
throw new Error('No business network specified');
4647
}
@@ -49,6 +50,11 @@ class Context {
4950
this.installedBusinessNetwork = installedBusinessNetwork;
5051
this.eventNumber = 0;
5152
this.contextId = uuid.v4();
53+
this.historianEnabled = true;
54+
if (installedBusinessNetwork.getDefinition().getMetadata().getPackageJson().disableHistorian === true) {
55+
LOG.debug(method, 'Historian disabled');
56+
this.historianEnabled = false;
57+
}
5258
}
5359

5460
/**

packages/composer-runtime/lib/engine.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class Engine {
173173
}
174174

175175
// This step executes the start business network transaction. This is a no-op, but records
176-
// the event into the transaction registry and historian.
176+
// the event into the transaction registry and optionally historian.
177177
LOG.debug(method, 'Executing start business network transaction');
178178
await this.submitTransaction(context, [JSON.stringify(transactionData)]);
179179

packages/composer-runtime/lib/engine.transactions.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,21 @@ class EngineTransactions {
6464
LOG.debug(method, 'Getting default transaction registry for ' + transactionFQT);
6565
const txRegistry = await registryManager.get('Transaction', transactionFQT);
6666

67-
LOG.debug(method, 'Getting historian registry');
68-
const historian = await registryManager.get('Asset', 'org.hyperledger.composer.system.HistorianRecord');
69-
70-
// Form the historian record
71-
const record = this._createHistorianRecord(context, transaction);
67+
let historian;
68+
let historianRecord;
69+
let canHistorianAdd = null; // this means it's good otherwise it will contain an error object
70+
if (context.historianEnabled === undefined || context.historianEnabled) {
71+
LOG.debug(method, 'Getting historian registry');
72+
historian = await registryManager.get('Asset', 'org.hyperledger.composer.system.HistorianRecord');
73+
74+
// Form the historian record
75+
historianRecord = this._createHistorianRecord(context, transaction);
76+
canHistorianAdd = await historian.testAdd(historianRecord);
77+
}
7278

73-
// check that we can add to both these registries ahead of time
74-
LOG.debug(method, 'Validating ability to create in Transaction and Historian registries');
79+
// check that we can add to both these registries ahead of time
80+
LOG.debug(method, 'Validating ability to create in Transaction and optionally Historian registries');
7581
let canTxAdd = await txRegistry.testAdd(transaction);
76-
let canHistorianAdd = await historian.testAdd(record);
7782

7883
if (canTxAdd || canHistorianAdd){
7984
throw canTxAdd ? canTxAdd : canHistorianAdd;
@@ -86,12 +91,14 @@ class EngineTransactions {
8691
LOG.debug(method, 'Storing executed transaction in Transaction registry');
8792
await txRegistry.add(transaction, {noTest: true});
8893

89-
// Update the historian record before we store it.
90-
this._updateHistorianRecord(context, record);
94+
if (context.historianEnabled === undefined || context.historianEnabled) {
95+
// Update the historian record before we store it.
96+
this._updateHistorianRecord(context, historianRecord);
9197

92-
// Store the historian record in the historian registry.
93-
LOG.debug(method, 'Storing Historian record in Historian registry');
94-
await historian.add(record, {noTest: true});
98+
// Store the historian record in the historian registry.
99+
LOG.debug(method, 'Storing Historian record in Historian registry');
100+
await historian.add(historianRecord, {noTest: true});
101+
}
95102

96103
context.clearTransaction();
97104
LOG.exit(method, returnValue);

packages/composer-runtime/test/context.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const AccessController = require('../lib/accesscontroller');
1919
const AclManager = require('composer-common').AclManager;
2020
const Api = require('../lib/api');
2121
const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefinition;
22+
const BusinessNetworkMetadata = require('composer-common').BusinessNetworkMetadata;
2223
const CompiledAclBundle = require('../lib/compiledaclbundle');
2324
const CompiledQueryBundle = require('../lib/compiledquerybundle');
2425
const CompiledScriptBundle = require('../lib/compiledscriptbundle');
@@ -75,6 +76,43 @@ describe('Context', () => {
7576
new Context(mockEngine);
7677
}).should.throw(/No business network/i);
7778
});
79+
80+
it('should enable historian if no request to disable is present', () => {
81+
context.historianEnabled.should.be.true;
82+
});
83+
84+
it('should disable historian if disableHistorian set to true', () => {
85+
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
86+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
87+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
88+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
89+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
90+
mockBusinessNetworkMetadata.getPackageJson.returns({'disableHistorian': true});
91+
context = new Context(mockEngine, mockInstalledBusinessNetwork);
92+
context.historianEnabled.should.be.false;
93+
});
94+
95+
it('should enable historian if disableHistorian set to false', () => {
96+
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
97+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
98+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
99+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
100+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
101+
mockBusinessNetworkMetadata.getPackageJson.returns({'disableHistorian': false});
102+
context = new Context(mockEngine, mockInstalledBusinessNetwork);
103+
context.historianEnabled.should.be.true;
104+
});
105+
106+
it('should disable historian if disableHistorian set to a non boolean', () => {
107+
const mockInstalledBusinessNetwork = sinon.createStubInstance(InstalledBusinessNetwork);
108+
const mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
109+
mockInstalledBusinessNetwork.getDefinition.returns(mockBusinessNetworkDefinition);
110+
const mockBusinessNetworkMetadata = sinon.createStubInstance(BusinessNetworkMetadata);
111+
mockBusinessNetworkDefinition.getMetadata.returns(mockBusinessNetworkMetadata);
112+
mockBusinessNetworkMetadata.getPackageJson.returns({'disableHistorian': 1});
113+
context = new Context(mockEngine, mockInstalledBusinessNetwork);
114+
context.historianEnabled.should.be.true;
115+
});
78116
});
79117

80118
describe('#getFunction', () => {

packages/composer-runtime/test/engine.transactions.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ describe('EngineTransactions', () => {
225225
.should.be.rejectedWith(/Invalid arguments "\["no","args","supported","here"\]" to function "submitTransaction", expecting "\["serializedResource"\]"/);
226226
});
227227

228-
it('should execute a transaction that does not return a value', async () => {
228+
it('should execute a transaction that does not return a value and write to historian if flag undefined', async () => {
229229
mockRegistryManager.get.withArgs('Transaction', 'org.acme.MyTransaction').resolves(mockTransactionRegistry);
230230
const resolvedTransaction = factory.newTransaction('org.acme', 'MyTransaction');
231231
mockResolver.resolve.resolves(resolvedTransaction);
@@ -246,6 +246,48 @@ describe('EngineTransactions', () => {
246246
sinon.assert.calledWith(mockHistorian.add, sinon.match(historianRecord => historianRecord.getFullyQualifiedIdentifier() === 'org.hyperledger.composer.system.HistorianRecord#TX_1'), { noTest: true });
247247
});
248248

249+
it('should execute a transaction that does not return a value and write to historian if explicitly requested', async () => {
250+
mockContext.historianEnabled = true;
251+
mockRegistryManager.get.withArgs('Transaction', 'org.acme.MyTransaction').resolves(mockTransactionRegistry);
252+
const resolvedTransaction = factory.newTransaction('org.acme', 'MyTransaction');
253+
mockResolver.resolve.resolves(resolvedTransaction);
254+
const result = await engine.invoke(mockContext, 'submitTransaction', [JSON.stringify({
255+
$class: 'org.acme.MyTransaction',
256+
transactionId: 'TX_1',
257+
timestamp: new Date(0).toISOString(),
258+
value: 'hello world'
259+
})]);
260+
should.equal(result, undefined);
261+
sinon.assert.calledOnce(mockTransactionRegistry.testAdd);
262+
sinon.assert.calledWith(mockTransactionRegistry.testAdd, sinon.match(transaction => transaction.getFullyQualifiedIdentifier() === 'org.acme.MyTransaction#TX_1'));
263+
sinon.assert.calledOnce(mockHistorian.testAdd);
264+
sinon.assert.calledWith(mockHistorian.testAdd, sinon.match(historianRecord => historianRecord.getFullyQualifiedIdentifier() === 'org.hyperledger.composer.system.HistorianRecord#TX_1'));
265+
sinon.assert.calledOnce(mockTransactionRegistry.add);
266+
sinon.assert.calledWith(mockTransactionRegistry.add, sinon.match(transaction => transaction.getFullyQualifiedIdentifier() === 'org.acme.MyTransaction#TX_1'), { noTest: true });
267+
sinon.assert.calledOnce(mockHistorian.add);
268+
sinon.assert.calledWith(mockHistorian.add, sinon.match(historianRecord => historianRecord.getFullyQualifiedIdentifier() === 'org.hyperledger.composer.system.HistorianRecord#TX_1'), { noTest: true });
269+
});
270+
271+
it('should not write to historian if disabled', async () => {
272+
mockContext.historianEnabled = false;
273+
mockRegistryManager.get.withArgs('Transaction', 'org.acme.MyTransaction').resolves(mockTransactionRegistry);
274+
const resolvedTransaction = factory.newTransaction('org.acme', 'MyTransaction');
275+
mockResolver.resolve.resolves(resolvedTransaction);
276+
const result = await engine.invoke(mockContext, 'submitTransaction', [JSON.stringify({
277+
$class: 'org.acme.MyTransaction',
278+
transactionId: 'TX_1',
279+
timestamp: new Date(0).toISOString(),
280+
value: 'hello world'
281+
})]);
282+
should.equal(result, undefined);
283+
sinon.assert.calledOnce(mockTransactionRegistry.testAdd);
284+
sinon.assert.calledWith(mockTransactionRegistry.testAdd, sinon.match(transaction => transaction.getFullyQualifiedIdentifier() === 'org.acme.MyTransaction#TX_1'));
285+
sinon.assert.notCalled(mockHistorian.testAdd);
286+
sinon.assert.calledOnce(mockTransactionRegistry.add);
287+
sinon.assert.calledWith(mockTransactionRegistry.add, sinon.match(transaction => transaction.getFullyQualifiedIdentifier() === 'org.acme.MyTransaction#TX_1'), { noTest: true });
288+
sinon.assert.notCalled(mockHistorian.add);
289+
});
290+
249291
it('should execute a transaction that returns a value', async () => {
250292
mockRegistryManager.get.withArgs('Transaction', 'org.acme.MyTransactionThatReturnsString').resolves(mockTransactionRegistry);
251293
const resolvedTransaction = factory.newTransaction('org.acme', 'MyTransactionThatReturnsString');

0 commit comments

Comments
 (0)