This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch
3188-performance-issue-large-stored-tree-nodes-impacting-ui
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to
refs/heads/3188-performance-issue-large-stored-tree-nodes-impacting-ui by this
push:
new 34179aef18 fix(#3188): Do not store nodes for
RuntimeResolvableTreeInputStaticProperty anymore
34179aef18 is described below
commit 34179aef18afdd07e5a534265874c2a499d0d48f
Author: Philipp Zehnder <[email protected]>
AuthorDate: Wed Aug 28 13:33:29 2024 +0200
fix(#3188): Do not store nodes for RuntimeResolvableTreeInputStaticProperty
anymore
---
ui/cypress/support/utils/connect/OpcUaUtils.ts | 100 +++++++++++++++++++++
.../connect/opcua/opcAdapterConfiguration.spec.ts | 27 ++----
.../connect/opcua/startAndEditOpcAdapters.spec.ts | 60 ++-----------
.../connect/opcua/staticPropertyTreeNodesTest.ts | 85 ++++++++++++++++++
...atic-runtime-resolvable-tree-input.component.ts | 16 ++--
.../static-tree-input-browse-nodes.component.ts | 1 -
6 files changed, 205 insertions(+), 84 deletions(-)
diff --git a/ui/cypress/support/utils/connect/OpcUaUtils.ts
b/ui/cypress/support/utils/connect/OpcUaUtils.ts
new file mode 100644
index 0000000000..5c70b8c655
--- /dev/null
+++ b/ui/cypress/support/utils/connect/OpcUaUtils.ts
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { AdapterInput } from '../../model/AdapterInput';
+import { ConnectUtils } from './ConnectUtils';
+import { ErrorMessageUtils } from '../ErrorMessageUtils';
+import { StaticPropertyUtils } from '../userInput/StaticPropertyUtils';
+import { TreeNodeUserInputBuilder } from
'../../builder/TreeNodeUserInputBuilder';
+import { AdapterBuilder } from '../../builder/AdapterBuilder';
+import { ParameterUtils } from '../ParameterUtils';
+
+export class OpcUaUtils {
+ public static setUpInitialConfiguration(adapterInput: AdapterInput) {
+ ConnectUtils.goToConnect();
+ ConnectUtils.goToNewAdapterPage();
+ ConnectUtils.selectAdapter(adapterInput.adapterType);
+
+ // Wait for the first static property to be rendered
+ cy.dataCy(adapterInput.adapterConfiguration[0].selector).should(
+ 'be.visible',
+ );
+ // Validate that no error is not shown when nothing is configured
+ cy.dataCy('reloading-nodes', { timeout: 3000 }).should('not.exist');
+ ErrorMessageUtils.getExceptionComponent().should('not.exist');
+
+ StaticPropertyUtils.input(adapterInput.adapterConfiguration);
+ }
+
+ public static getAdapterBuilderWithTreeNodes(pullMode: boolean) {
+ const builder = OpcUaUtils.getBaseAdapterConfigBuilder(pullMode);
+ builder.addTreeNode(
+ TreeNodeUserInputBuilder.create(
+ 'Objects',
+ TreeNodeUserInputBuilder.create(
+ 'OpcPlc',
+ TreeNodeUserInputBuilder.create(
+ 'Telemetry',
+ TreeNodeUserInputBuilder.create('Basic').addChildren(
+ TreeNodeUserInputBuilder.create(
+ 'AlternatingBoolean',
+ ),
+ TreeNodeUserInputBuilder.create('StepUp'),
+ TreeNodeUserInputBuilder.create(
+ 'RandomSignedInt32',
+ ),
+ TreeNodeUserInputBuilder.create(
+ 'RandomUnsignedInt32',
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ return builder.build();
+ }
+
+ public static getBaseAdapterConfigBuilder(
+ pullMode: boolean,
+ ): AdapterBuilder {
+ const host: string = ParameterUtils.get('localhost', 'opcua');
+
+ const builder = AdapterBuilder.create('OPC_UA').setName('OPC UA Test');
+
+ if (pullMode) {
+ builder.addInput('radio', 'adapter_type-pull_mode', '');
+ builder.addInput('input', 'undefined-PULLING_INTERVAL-0', '1000');
+ } else {
+ builder.addInput('radio', 'adapter_type-subscription_mode', '');
+ }
+
+ builder
+ .addInput('radio', 'access_mode-none', '')
+ .addInput('radio', 'opc_host_or_url-url', '')
+ .addInput(
+ 'input',
+ 'undefined-OPC_SERVER_URL-0',
+ 'opc.tcp://' + host + ':50000',
+ );
+
+ builder.setAutoAddTimestampPropery();
+
+ return builder;
+ }
+}
diff --git a/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.spec.ts
b/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.spec.ts
index 8889c18542..9fb08f78bd 100644
--- a/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.spec.ts
+++ b/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.spec.ts
@@ -20,10 +20,9 @@ import { ConnectUtils } from
'../../../support/utils/connect/ConnectUtils';
import { ParameterUtils } from '../../../support/utils/ParameterUtils';
import { AdapterBuilder } from '../../../support/builder/AdapterBuilder';
import { TreeNodeUserInputBuilder } from
'../../../support/builder/TreeNodeUserInputBuilder';
-import { StaticPropertyUtils } from
'../../../support/utils/userInput/StaticPropertyUtils';
import { TreeStaticPropertyUtils } from
'../../../support/utils/userInput/TreeStaticPropertyUtils';
import { ErrorMessageUtils } from '../../../support/utils/ErrorMessageUtils';
-import { AdapterInput } from '../../../support/model/AdapterInput';
+import { OpcUaUtils } from '../../../support/utils/connect/OpcUaUtils';
describe('Test OPC-UA Adapter Configuration', () => {
beforeEach('Setup Test', () => {
@@ -51,7 +50,7 @@ describe('Test OPC-UA Adapter Configuration', () => {
);
const adapterInput = adapterBuilder.build();
- setUpInitialConfiguration(adapterInput);
+ OpcUaUtils.setUpInitialConfiguration(adapterInput);
TreeStaticPropertyUtils.validateAmountOfSelectedNodes(2);
@@ -81,7 +80,7 @@ describe('Test OPC-UA Adapter Configuration', () => {
it('Test OPC-UA Text Editor', () => {
const adapterInput = getAdapterBuilder().build();
- setUpInitialConfiguration(adapterInput);
+ OpcUaUtils.setUpInitialConfiguration(adapterInput);
TreeStaticPropertyUtils.treeEditor().should('be.visible');
TreeStaticPropertyUtils.textEditor().should('not.exist');
@@ -123,7 +122,7 @@ describe('Test OPC-UA Adapter Configuration', () => {
it('Test OPC-UA Node does not exist', () => {
const adapterInput = getAdapterBuilder().build();
- setUpInitialConfiguration(adapterInput);
+ OpcUaUtils.setUpInitialConfiguration(adapterInput);
// Switch to text editor
TreeStaticPropertyUtils.switchToTextEditor();
@@ -137,7 +136,7 @@ describe('Test OPC-UA Adapter Configuration', () => {
it('Test OPC-UA Wrong Node Id Format', () => {
const adapterInput = getAdapterBuilder().build();
- setUpInitialConfiguration(adapterInput);
+ OpcUaUtils.setUpInitialConfiguration(adapterInput);
// Switch to text editor
TreeStaticPropertyUtils.switchToTextEditor();
@@ -166,19 +165,3 @@ const getAdapterBuilder = () => {
)
.setAutoAddTimestampPropery();
};
-
-const setUpInitialConfiguration = (adapterInput: AdapterInput) => {
- ConnectUtils.goToConnect();
- ConnectUtils.goToNewAdapterPage();
- ConnectUtils.selectAdapter(adapterInput.adapterType);
-
- // Wait for the first static property to be rendered
- cy.dataCy(adapterInput.adapterConfiguration[0].selector).should(
- 'be.visible',
- );
- // Validate that no error is not shown when nothing is configured
- cy.dataCy('reloading-nodes', { timeout: 3000 }).should('not.exist');
- ErrorMessageUtils.getExceptionComponent().should('not.exist');
-
- StaticPropertyUtils.input(adapterInput.adapterConfiguration);
-};
diff --git a/ui/cypress/tests/connect/opcua/startAndEditOpcAdapters.spec.ts
b/ui/cypress/tests/connect/opcua/startAndEditOpcAdapters.spec.ts
index 4e8ccba5fe..0930395268 100644
--- a/ui/cypress/tests/connect/opcua/startAndEditOpcAdapters.spec.ts
+++ b/ui/cypress/tests/connect/opcua/startAndEditOpcAdapters.spec.ts
@@ -17,13 +17,12 @@
*/
import { ConnectUtils } from '../../../support/utils/connect/ConnectUtils';
-import { ParameterUtils } from '../../../support/utils/ParameterUtils';
-import { AdapterBuilder } from '../../../support/builder/AdapterBuilder';
import { TreeNodeUserInputBuilder } from
'../../../support/builder/TreeNodeUserInputBuilder';
import { ConnectBtns } from '../../../support/utils/connect/ConnectBtns';
import { TreeStaticPropertyUtils } from
'../../../support/utils/userInput/TreeStaticPropertyUtils';
import { ConnectEventSchemaUtils } from
'../../../support/utils/connect/ConnectEventSchemaUtils';
import { AdapterInput } from '../../../support/model/AdapterInput';
+import { OpcUaUtils } from '../../../support/utils/connect/OpcUaUtils';
describe('Test starting and editing OPC-UA Adapters in different
configurations', () => {
beforeEach('Setup Test', () => {
@@ -31,12 +30,12 @@ describe('Test starting and editing OPC-UA Adapters in
different configurations'
});
it('Create OPC-UA Adapter Tree Editor Pull Mode', () => {
- const adapterInput = getAdapterBuilderWithTreeNodes(true);
+ const adapterInput = OpcUaUtils.getAdapterBuilderWithTreeNodes(true);
startAdapterTest(adapterInput);
});
it('Create OPC-UA Adapter Tree Editor Subscription Mode', () => {
- const adapterInput = getAdapterBuilderWithTreeNodes(false);
+ const adapterInput = OpcUaUtils.getAdapterBuilderWithTreeNodes(false);
startAdapterTest(adapterInput);
});
@@ -51,7 +50,7 @@ describe('Test starting and editing OPC-UA Adapters in
different configurations'
});
it('Edit OPC-UA Adapter created with Tree editor', () => {
- const adapterInput = getAdapterBuilderWithTreeNodes(true);
+ const adapterInput = OpcUaUtils.getAdapterBuilderWithTreeNodes(true);
editAdapterTest(adapterInput);
});
@@ -63,29 +62,6 @@ describe('Test starting and editing OPC-UA Adapters in
different configurations'
});
});
-const getAdapterBuilderWithTreeNodes = (pullMode: boolean) => {
- const builder = getBaseAdapterConfigBuilder(pullMode);
- builder.addTreeNode(
- TreeNodeUserInputBuilder.create(
- 'Objects',
- TreeNodeUserInputBuilder.create(
- 'OpcPlc',
- TreeNodeUserInputBuilder.create(
- 'Telemetry',
- TreeNodeUserInputBuilder.create('Basic').addChildren(
- TreeNodeUserInputBuilder.create('AlternatingBoolean'),
- TreeNodeUserInputBuilder.create('StepUp'),
- TreeNodeUserInputBuilder.create('RandomSignedInt32'),
- TreeNodeUserInputBuilder.create('RandomUnsignedInt32'),
- ),
- ),
- ),
- ),
- );
-
- return builder.build();
-};
-
/**
* The start adapter test expects an adapter input with the same schema
* description for all tests. Only the opc ua related options might differ.
@@ -117,7 +93,7 @@ const editAdapterTest = (adapterInput: AdapterInput) => {
};
const getAdapterBuilderWithTextNodes = (pullMode: boolean) => {
- const builder = getBaseAdapterConfigBuilder(pullMode);
+ const builder = OpcUaUtils.getBaseAdapterConfigBuilder(pullMode);
builder.addTreeNode(
TreeNodeUserInputBuilder.create(
'ns=3;s=AlternatingBoolean',
@@ -139,29 +115,3 @@ const getAdapterBuilderWithTextNodes = (pullMode: boolean)
=> {
return builder.build();
};
-
-const getBaseAdapterConfigBuilder = (pullMode: boolean): AdapterBuilder => {
- const host: string = ParameterUtils.get('localhost', 'opcua');
-
- const builder = AdapterBuilder.create('OPC_UA').setName('OPC UA Test');
-
- if (pullMode) {
- builder.addInput('radio', 'adapter_type-pull_mode', '');
- builder.addInput('input', 'undefined-PULLING_INTERVAL-0', '1000');
- } else {
- builder.addInput('radio', 'adapter_type-subscription_mode', '');
- }
-
- builder
- .addInput('radio', 'access_mode-none', '')
- .addInput('radio', 'opc_host_or_url-url', '')
- .addInput(
- 'input',
- 'undefined-OPC_SERVER_URL-0',
- 'opc.tcp://' + host + ':50000',
- );
-
- builder.setAutoAddTimestampPropery();
-
- return builder;
-};
diff --git a/ui/cypress/tests/connect/opcua/staticPropertyTreeNodesTest.ts
b/ui/cypress/tests/connect/opcua/staticPropertyTreeNodesTest.ts
new file mode 100644
index 0000000000..13c6b39150
--- /dev/null
+++ b/ui/cypress/tests/connect/opcua/staticPropertyTreeNodesTest.ts
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { ConnectUtils } from '../../../support/utils/connect/ConnectUtils';
+import { ConnectEventSchemaUtils } from
'../../../support/utils/connect/ConnectEventSchemaUtils';
+import { OpcUaUtils } from '../../../support/utils/connect/OpcUaUtils';
+
+describe('Test Tree Node Configuration', () => {
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+ });
+
+ /**
+ * This test creates an OPC UA adapter and ensures that no node
information is sent to the backend,
+ * to reduce the size of the stored adapter.
+ */
+ it('Tree nodes should not be submitted to the backend', () => {
+ const adapterInput = OpcUaUtils.getAdapterBuilderWithTreeNodes(true);
+
+ OpcUaUtils.setUpInitialConfiguration(adapterInput);
+
+ cy.intercept(
+ 'POST',
+ 'streampipes-backend/api/v2/connect/master/guess/schema',
+ ).as('guessSchema');
+
+ ConnectUtils.finishAdapterSettings();
+
+ cy.wait('@guessSchema').then(interception => {
+ validateNodesAreEmptyInAdapterDescriptionBody(interception);
+ });
+
+ cy.intercept(
+ 'POST',
+ 'streampipes-backend/api/v2/connect/master/adapters',
+ ).as('startAdapter');
+
+ ConnectEventSchemaUtils.addTimestampProperty();
+ ConnectUtils.finishEventSchemaConfiguration();
+ ConnectUtils.startAdapter(adapterInput);
+
+ cy.wait('@startAdapter').then(interception => {
+ validateNodesAreEmptyInAdapterDescriptionBody(interception);
+ });
+ });
+});
+
+/**
+ * This method intercepts the http requests, validates that it was
successfully.
+ * Further it validates that the client does not send node information to the
backend.
+ */
+const validateNodesAreEmptyInAdapterDescriptionBody = interception => {
+ expect(interception.response.statusCode).to.equal(200);
+
+ const adapterDescription = interception.request.body;
+ console.log(adapterDescription);
+
+ const runtimeResolvableTreeInput = adapterDescription.config.find(
+ (configItem: any) =>
+ configItem['@class'] ===
+
'org.apache.streampipes.model.staticproperty.RuntimeResolvableTreeInputStaticProperty',
+ );
+
+ expect(runtimeResolvableTreeInput).to.exist;
+
+ expect(runtimeResolvableTreeInput.nodes).to.be.an('array').that.is.empty;
+
+
expect(runtimeResolvableTreeInput.latestFetchedNodes).to.be.an('array').that
+ .is.empty;
+};
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
index 1a1a60ea85..8d0ff9d3b2 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
@@ -41,6 +41,12 @@ export class StaticRuntimeResolvableTreeInputComponent
editorMode: 'tree' | 'text' = 'tree';
+ // The following two arrays store the fetched nodes from the backend to
+ // present them to the user in the UI. For performance reasons, the nodes
+ // should not be stored in the static property object
+ latestFetchedNodes = [];
+ nodes = [];
+
@ViewChild('staticTreeInputBrowseNodesComponent')
private staticTreeInputBrowseNodesComponent:
StaticTreeInputBrowseNodesComponent;
@@ -87,16 +93,13 @@ export class StaticRuntimeResolvableTreeInputComponent
staticProperty.latestFetchedNodes &&
staticProperty.latestFetchedNodes.length > 0
) {
- this.staticProperty.latestFetchedNodes =
- staticProperty.latestFetchedNodes;
+ this.latestFetchedNodes = staticProperty.latestFetchedNodes;
if (node) {
node.children = staticProperty.latestFetchedNodes;
}
} else {
- this.staticProperty.nodes = staticProperty.nodes;
- this.staticTreeInputBrowseNodesComponent?.updateNodes(
- this.staticProperty.nodes,
- );
+ this.nodes = staticProperty.nodes;
+ this.staticTreeInputBrowseNodesComponent?.updateNodes(this.nodes);
}
this.staticTreeInputBrowseNodesComponent?.refreshTree();
@@ -175,6 +178,7 @@ export class StaticRuntimeResolvableTreeInputComponent
private resetStaticPropertyState(): void {
this.staticProperty.latestFetchedNodes = [];
+ this.latestFetchedNodes = [];
this.staticProperty.nextBaseNodeToResolve = undefined;
}
}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
index 478c719509..de04432f80 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
@@ -78,7 +78,6 @@ export class StaticTreeInputBrowseNodesComponent implements
OnInit {
}
updateNodes(nodes: TreeInputNode[]) {
- console.log(nodes);
this.dataSource.data = nodes;
}