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;
     }
 

Reply via email to