Repository: apache/streampipes
Pull request #1517 (2023-04-24 17:30:51)

[FEATURE] add division as operation for data harmonization rules
<!--
  ~ 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.
-->
When creating an adapter in StreamPipes Connect, we allow to add some transformation rules that directly modify modify data when ingesting. Hereby, we support basic mathematic operations.
This PR extends this functionality by the division operator.

### 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/.github/workflows/python-docs.yml b/.github/workflows/python-docs.yml
index 19023a3098..6581ee030e 100644
--- a/.github/workflows/python-docs.yml
+++ b/.github/workflows/python-docs.yml
@@ -56,8 +56,7 @@ jobs:
       - name: Build Python docs
         working-directory: ./streampipes-client-python
         run: |
-          mike deploy dev latest -u --deploy-prefix ./streampipes-client-python/docs-tmp -b build-python-docs-${{ github.run_id }}
-          mike set-default latest --deploy-prefix ./streampipes-client-python/docs-tmp -b build-python-docs-${{ github.run_id }}
+          mike deploy dev -u --deploy-prefix ./streampipes-client-python/docs-tmp -b build-python-docs-${{ github.run_id }}
           git reset --hard  # undo local changes that delete generated doc files
 
       - name: Publish Python docs as artifact
diff --git a/streampipes-extensions-management/src/main/java/org/apache/streampipes/extensions/management/connect/adapter/preprocessing/transform/value/CorrectionValueTransformationRule.java b/streampipes-extensions-management/src/main/java/org/apache/streampipes/extensions/management/connect/adapter/preprocessing/transform/value/CorrectionValueTransformationRule.java
index 4b1f6f838a..e720e4b27c 100644
--- a/streampipes-extensions-management/src/main/java/org/apache/streampipes/extensions/management/connect/adapter/preprocessing/transform/value/CorrectionValueTransformationRule.java
+++ b/streampipes-extensions-management/src/main/java/org/apache/streampipes/extensions/management/connect/adapter/preprocessing/transform/value/CorrectionValueTransformationRule.java
@@ -49,26 +49,22 @@ public class CorrectionValueTransformationRule implements ValueTransformationRul
     if (eventKey.size() == 1) {
       try {
         Object obj = event.get(eventKey.get(0));
-        double old = 0d;
+        double old;
         if (obj instanceof Number) {
           old = ((Number) obj).doubleValue();
+        } else {
+          throw new RuntimeException(
+              String.format("Selected property `%s` does not contain a numeric value: `%s", eventKey.get(0), obj)
+          );
         }
 
-        double corrected = 0d;
-        switch (operator) {
-          case "MULTIPLY":
-            corrected = old * correctionValue;
-            break;
-          case "ADD":
-            corrected = old + correctionValue;
-            break;
-          case "SUBSTRACT":
-            corrected = old - correctionValue;
-            break;
-          default:
-            corrected = old;
-            break;
-        }
+        double corrected = switch (operator) {
+          case "MULTIPLY" -> old * correctionValue;
+          case "ADD" -> old + correctionValue;
+          case "SUBTRACT" -> old - correctionValue;
+          case "DIVIDE" -> old / correctionValue;
+          default -> old;
+        };
 
         event.put(eventKey.get(0), corrected);
       } catch (ClassCastException e) {
diff --git a/ui/README.md b/ui/README.md
index 91c19f6eb6..3f3ce4304e 100644
--- a/ui/README.md
+++ b/ui/README.md
@@ -43,4 +43,4 @@ To test your local version of the UI execute the following commands:
 Running `npm install` will cause a pre-commit hook to be created.
 This hook ensures that if you want to commit changes to this repository, these changes are compliant with our [formatting](https://prettier.io/) and [linting](https://eslint.org/) rules.
 
-If you see errors there, you can try to run `npm run format:fix` and `npm run lint:fix` to fix these issues automatically. Otherwise you have to run `npm run format` and `npm run lint` to get additional information about the problems.
+If you see errors there, you can try to run `npm run format:fix` and `npm run lint:fix` to fix these issues automatically. Otherwise, you have to run `npm run format` and `npm run lint` to get additional information about the problems.
diff --git a/ui/src/app/connect/dialog/edit-event-property/components/edit-value-transformation/edit-correction-value/edit-correction-value.component.ts b/ui/src/app/connect/dialog/edit-event-property/components/edit-value-transformation/edit-correction-value/edit-correction-value.component.ts
index 90e17883ce..87b31c9fd1 100644
--- a/ui/src/app/connect/dialog/edit-event-property/components/edit-value-transformation/edit-correction-value/edit-correction-value.component.ts
+++ b/ui/src/app/connect/dialog/edit-event-property/components/edit-value-transformation/edit-correction-value/edit-correction-value.component.ts
@@ -30,7 +30,8 @@ export class EditCorrectionValueComponent {
     operators = [
         { value: 'MULTIPLY', viewValue: 'Multiply' },
         { value: 'ADD', viewValue: 'Add' },
-        { value: 'SUBSTRACT', viewValue: 'Substract' },
+        { value: 'SUBTRACT', viewValue: 'Subtract' },
+        { value: 'DIVIDE', viewValue: 'Divide' },
     ];
 
     constructor() {}
diff --git a/ui/src/app/connect/dialog/edit-event-property/edit-event-property.component.ts b/ui/src/app/connect/dialog/edit-event-property/edit-event-property.component.ts
index df8839db8d..b2c98542c5 100644
--- a/ui/src/app/connect/dialog/edit-event-property/edit-event-property.component.ts
+++ b/ui/src/app/connect/dialog/edit-event-property/edit-event-property.component.ts
@@ -159,7 +159,7 @@ export class EditEventPropertyComponent implements OnInit {
         this.property.label = this.cachedProperty.label;
         this.property.description = this.cachedProperty.description;
 
-        // remove undefined from domain properies array
+        // remove undefined from domain properties array
         this.property.domainProperties =
             this.cachedProperty.domainProperties.filter(n => n);
         this.property.runtimeName = this.cachedProperty.runtimeName;


Test patch:
diff --git a/streampipes-extensions-management/src/test/java/org/apache/streampipes/extensions/management/connect/adapter/transform/value/CorrectionValueTest.java b/streampipes-extensions-management/src/test/java/org/apache/streampipes/extensions/management/connect/adapter/transform/value/CorrectionValueTest.java
new file mode 100644
index 0000000000..5bc26c26d8
--- /dev/null
+++ b/streampipes-extensions-management/src/test/java/org/apache/streampipes/extensions/management/connect/adapter/transform/value/CorrectionValueTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.streampipes.extensions.management.connect.adapter.transform.value;
+
+import org.apache.streampipes.extensions.management.connect.adapter.preprocessing.transform.value.CorrectionValueTransformationRule;
+import org.apache.streampipes.model.schema.EventProperty;
+import org.apache.streampipes.model.schema.EventPropertyPrimitive;
+import org.apache.streampipes.model.schema.EventSchema;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+public class CorrectionValueTest {
+
+  private Map<String, Object> event;
+
+  private final String propertyNameBasicValue = "basicValue";
+  private final String propertyNameOtherValue = "otherValue";
+
+  @Before
+  public void setUp() {
+
+    EventSchema eventSchema = new EventSchema();
+    EventProperty eventProperty = new EventPropertyPrimitive();
+    eventProperty.setLabel(propertyNameBasicValue);
+    eventProperty.setRuntimeName(propertyNameBasicValue);
+    eventSchema.addEventProperty(eventProperty);
+
+    EventProperty eventPropertyOther = new EventPropertyPrimitive();
+    eventPropertyOther.setLabel(propertyNameBasicValue);
+    eventPropertyOther.setRuntimeName(propertyNameBasicValue);
+    eventSchema.addEventProperty(eventPropertyOther);
+
+    event = new HashMap<>();
+    event.put(propertyNameBasicValue, 100.0);
+    event.put(propertyNameOtherValue, "Hello");
+  }
+
+  @Test
+  public void testAdd() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+         List.of(propertyNameBasicValue),
+         10.0,
+         "ADD"
+     );
+
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(110.0, resultEvent.get(propertyNameBasicValue));
+  }
+
+  @Test
+  public void testSubtract() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameBasicValue),
+        10.0,
+        "SUBTRACT"
+    );
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(90.0, resultEvent.get(propertyNameBasicValue));
+  }
+
+  @Test
+  public void testMultiply() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameBasicValue),
+        1.5,
+        "MULTIPLY"
+    );
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(150.0, resultEvent.get(propertyNameBasicValue));
+  }
+
+  @Test
+  public void testDivide() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameBasicValue),
+        5,
+        "DIVIDE"
+    );
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(20.0, resultEvent.get(propertyNameBasicValue));
+  }
+
+  @Test
+  public void testDivideByZero() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameBasicValue),
+        0.0,
+        "DIVIDE"
+    );
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(Double.POSITIVE_INFINITY, resultEvent.get(propertyNameBasicValue));
+  }
+
+  @Test
+  public void testNonNumericValue() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameOtherValue),
+        10.0,
+        "ADD"
+    );
+    assertThrows (
+        String.format("Selected property `%s` does not contain a numeric value: `%s", propertyNameOtherValue, "Hello"),
+        RuntimeException.class,
+        () -> correctionRule.transform(event).get(propertyNameOtherValue)
+    );
+
+
+  }
+
+  @Test
+  public void testUnsupportedOperation() {
+
+    var correctionRule = new CorrectionValueTransformationRule(
+        List.of(propertyNameBasicValue),
+        10.0,
+        "TEST"
+    );
+    var resultEvent = correctionRule.transform(event);
+    assertNotNull(resultEvent);
+    assertEquals(100.0, resultEvent.get(propertyNameBasicValue));
+  }
+}