Repository: apache/streampipes (java)

Issue:
Bug when deleting nested properties in Adapter
### Body

If a user deletes a nested property and its parent, it can cause a null pointer exception.

I already provided a test case, see below.

## Extend the test code
- **File:** deleteTransformationRule.spec.ts
- **Test:** Test delete nested properties
- **Add the following code to the bottom of the test:**

```
ConnectBtns.refreshSchema().click();

// Test to delete the parent property
ConnectEventSchemaUtils.deleteProperty('parent');
cy.dataCy('schema-preview-result-event')
    .should(
        'have.text',
        '{"timestamp":\u00A01667904471000}',
    );
```

## Problem Details
- The problem occurs in the `DeleteTransformationRule.applyTransformation`
  - The value event is null
- Another problem is in the `SupportsNestedTransformationRule.applyNested`
  - A copy of the child object is added to the event in line 45, but the child could also be null

### StreamPipes Committer

I acknowledge that I am a maintainer/committer of the Apache StreamPipes project.

Pull request:
fix(#2886): Fix the deletion of nested properties in connect
<!--
  ~ 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.
  ~
  -->
  
  <!--
Thanks for contributing! Here are some tips you can follow to help us incorporate your contribution quickly and easily:
1. If this is your first time, please read our contributor guidelines:
    - https://streampipes.apache.org/getinvolved.html
    - https://cwiki.apache.org/confluence/display/STREAMPIPES/Getting+Started
2. Make sure the PR title is formatted like: `[#<GitHub issue id>] PR title ...`
3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., `[WIP][#<GitHub issue id>] PR title ...`.
4. Please write your PR title to summarize what this PR proposes/fixes.
5. Link the PR to the corresponding GitHub issue (if present) in the `Development` section in the right menu bar. 
6. Be sure to keep the PR description updated to reflect all changes.
7. If possible, provide a concise example to reproduce the issue for a faster review.
8. Make sure tests pass via `mvn clean install`.
9. (Optional) If the contribution is large, please file an Apache ICLA
    - http://apache.org/licenses/icla.pdf
-->

### Purpose
<!--
Please clarify what changes you are proposing and describe how those changes will address the issue.
Furthermore, describe potential consequences the changes might have.
-->
Fixes #2886 

### Remarks
<!--
Is there anything left we need to pay attention on?
Are there some references that might be important? E.g. links to Confluence, or discussions
on the mailing list or GitHub.
-->
PR introduces (a) breaking change(s): no

PR introduces (a) deprecation(s): no


Patch:
diff --git a/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/SupportsNestedTransformationRule.java b/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/SupportsNestedTransformationRule.java
index d332a79e42..a0494cff34 100644
--- a/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/SupportsNestedTransformationRule.java
+++ b/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/SupportsNestedTransformationRule.java
@@ -42,7 +42,10 @@ public abstract class SupportsNestedTransformationRule implements Transformation
           applyNested((Map<String, Object>) event.get(eventKey.get(0)), newKeysTmpList);
 
       event.remove(key);
-      event.put(key, newSubEvent);
+
+      if (newSubEvent != null && !newSubEvent.isEmpty()) {
+        event.put(key, newSubEvent);
+      }
     }
     return event;
   }
diff --git a/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRule.java b/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRule.java
index ae2d1c70d9..7fe7805baa 100644
--- a/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRule.java
+++ b/streampipes-connect-shared/src/main/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRule.java
@@ -38,6 +38,8 @@ public class DeleteTransformationRule extends SupportsNestedTransformationRule {
 
   @Override
   protected void applyTransformation(Map<String, Object> event, List<String> eventKeys) {
+    if (event != null) {
       event.remove(eventKeys.get(0));
+    }
   }
 }


Test patch:
diff --git a/streampipes-connect-shared/src/test/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRuleTest.java b/streampipes-connect-shared/src/test/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRuleTest.java
index 7253b33bd5..e8d5e1feb8 100644
--- a/streampipes-connect-shared/src/test/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRuleTest.java
+++ b/streampipes-connect-shared/src/test/java/org/apache/streampipes/connect/shared/preprocessing/transform/schema/DeleteTransformationRuleTest.java
@@ -44,13 +44,39 @@ public class DeleteTransformationRuleTest {
   public void transformNested() {
     var child = new HashMap<String, Object>();
     child.put("child", "value");
+    child.put("secondChild", "value");
     var event = new HashMap<String, Object>();
     event.put("parent", child);
+    event.put("keepProperty", "test");
+
+    var deleteRule = new DeleteTransformationRule(List.of("parent", "child"));
+
+    var result = deleteRule.apply(event);
+
+    assertEquals(2, result.keySet().size());
+  }
+
+  @Test
+  // verifying that mathod applyTransformation method works when passed a null event.
+  public void applyTransformationWithNullParameter() {
+    new DeleteTransformationRule(List.of())
+        .applyTransformation(null, List.of());
+  }
+
+  @Test
+  public void deleteNestedChildWithParentProperty() {
+    var child = new HashMap<String, Object>();
+    child.put("child", "value");
+    var event = new HashMap<String, Object>();
+    event.put("parent", child);
+    event.put("keepProperty", "test");
 
     var deleteRule = new DeleteTransformationRule(Arrays.asList("parent", "child"));
 
     var result = deleteRule.apply(event);
 
     assertEquals(1, result.keySet().size());
+    assertEquals("test", result.get("keepProperty"));
   }
+
 }
diff --git a/ui/cypress/tests/adapter/rules/deleteTransformationRule.spec.ts b/ui/cypress/tests/adapter/rules/deleteTransformationRule.spec.ts
index debbc5473f..2ece7e9a38 100644
--- a/ui/cypress/tests/adapter/rules/deleteTransformationRule.spec.ts
+++ b/ui/cypress/tests/adapter/rules/deleteTransformationRule.spec.ts
@@ -19,6 +19,7 @@ import { ConnectUtils } from '../../../support/utils/connect/ConnectUtils';
 import { FileManagementUtils } from '../../../support/utils/FileManagementUtils';
 import { ConnectEventSchemaUtils } from '../../../support/utils/ConnectEventSchemaUtils';
 import { AdapterBuilder } from '../../../support/builder/AdapterBuilder';
+import { ConnectBtns } from '../../../support/utils/connect/ConnectBtns';
 
 describe('Connect delete rule transformation', () => {
     beforeEach('Setup Test', () => {
@@ -72,5 +73,15 @@ describe('Connect delete rule transformation', () => {
             'have.text',
             '{\u00A0\u00A0\u00A0\u00A0"parent":\u00A0{},\u00A0\u00A0\u00A0\u00A0"timestamp":\u00A01667904471000}',
         );
+
+        ConnectBtns.refreshSchema().click();
+
+        // Test to delete the parent property
+        ConnectEventSchemaUtils.deleteProperty('parent');
+        cy.dataCy('schema-preview-result-event').should(
+            'have.text',
+
+            '{\u00A0\u00A0\u00A0\u00A0"timestamp":\u00A01667904471000}',
+        );
     });
 });