Repository: apache/streampipes
Pull request #1516 (2023-04-21 03:02:18)

Bump karma-chrome-launcher from 3.1.1 to 3.2.0 in /ui/projects/streampipes/platform-services
Bumps [karma-chrome-launcher](https://github.com/karma-runner/karma-chrome-launcher) from 3.1.1 to 3.2.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/karma-runner/karma-chrome-launcher/releases">karma-chrome-launcher's releases</a>.</em></p>
<blockquote>
<h2>v3.2.0</h2>
<h1><a href="https://github.com/karma-runner/karma-chrome-launcher/compare/v3.1.1...v3.2.0">3.2.0</a> (2023-04-20)</h1>
<h3>Features</h3>
<ul>
<li>add process.env.ProgramW6432 as root location for binaries (<a href="https://github.com/karma-runner/karma-chrome-launcher/commit/12a73dba261c78fdba4aff4dbb741ac87adfed01">12a73db</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/karma-runner/karma-chrome-launcher/blob/master/CHANGELOG.md">karma-chrome-launcher's changelog</a>.</em></p>
<blockquote>
<h1><a href="https://github.com/karma-runner/karma-chrome-launcher/compare/v3.1.1...v3.2.0">3.2.0</a> (2023-04-20)</h1>
<h3>Features</h3>
<ul>
<li>add process.env.ProgramW6432 as root location for binaries (<a href="https://github.com/karma-runner/karma-chrome-launcher/commit/12a73dba261c78fdba4aff4dbb741ac87adfed01">12a73db</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/karma-runner/karma-chrome-launcher/commit/e92a2b4c19d43fe47a3322a31f72d2a5bfcf77b0"><code>e92a2b4</code></a> chore(release): 3.2.0 [skip ci]</li>
<li><a href="https://github.com/karma-runner/karma-chrome-launcher/commit/12a73dba261c78fdba4aff4dbb741ac87adfed01"><code>12a73db</code></a> feat: add process.env.ProgramW6432 as root location for binaries</li>
<li><a href="https://github.com/karma-runner/karma-chrome-launcher/commit/d193ef328ed9cb6474b59137e106cf67319f5262"><code>d193ef3</code></a> build(deps): bump semver-regex from 3.1.3 to 3.1.4</li>
<li><a href="https://github.com/karma-runner/karma-chrome-launcher/commit/d1c05e372a4c3f1c784257f4127bab1ff0460afe"><code>d1c05e3</code></a> build(deps): bump engine.io and karma</li>
<li><a href="https://github.com/karma-runner/karma-chrome-launcher/commit/3f0cd9985703fae99d123530600c248f0df9209a"><code>3f0cd99</code></a> build(deps): bump qs and body-parser</li>
<li>See full diff in <a href="https://github.com/karma-runner/karma-chrome-launcher/compare/v3.1.1...v3.2.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=karma-chrome-launcher&package-manager=npm_and_yarn&previous-version=3.1.1&new-version=3.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


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/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml
new file mode 100644
index 0000000000..191d1599c1
--- /dev/null
+++ b/.github/workflows/stale-bot.yml
@@ -0,0 +1,39 @@
+# 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.
+
+name: 'Close and remind stale PRs'
+on:
+  schedule:
+    - cron: '0 1 * * *'
+  workflow_dispatch:
+
+permissions:
+  pull-requests: write
+
+jobs:
+  mark-stale-prs:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/stale@v8
+        with:
+          close-pr-message: "Hello there :wave:<br> <br> Unfortunately, we didn't hear back from you regarding your pull request, so we're closing it now. Don't worry, you can always reopen the PR at any time if you wish to continue working on it :raised_hands:.<br> <br> Please note that the branch associated with this pull request will not be deleted, so you can still access your changes and continue to work on them as needed :computer:.<br> <br> Thank you for your contributions to our project, and we hope to see you again soon!"
+          days-before-close: 7
+          days-before-stale: 21
+          exempt-pr-labels: 'blocked'
+          labels-to-remove-when-unstale: 'stale'
+          only-issue-labels: 'do-not-stale'
+          operations-per-run: 1000
+          stale-pr-label: 'stale'
+          stale-pr-message: "Hello there :wave:<br> <br> We noticed that it's been some time since activity occurred on your pull request :thinking:. In order to keep things moving forward, we're marking this PR as _stale_ and giving you 7 days to respond before it's automatically closed :alarm_clock:.<br> <br> Please take a moment to review your pull request and make any necessary updates or changes :man_technologist:. If you need more time or have any questions, please don't hesitate to let us know :speech_balloon:.<br> <br> Thank you for your contributions to our project, and we look forward to hearing back from you soon :pray:."
diff --git a/pom.xml b/pom.xml
index 356d900616..79d6a6b7ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,7 +84,7 @@
 	<jboss-logging-processor.version>2.2.1.Final</jboss-logging-processor.version>
 	<jersey.version>3.1.0</jersey.version>
 	<jetbrains-annotations.version>16.0.2</jetbrains-annotations.version>
-	<jetty.version>10.0.10</jetty.version>
+	<jetty.version>10.0.14</jetty.version>
 	<jgrapht.version>1.5.1</jgrapht.version>
 	<jjwt.version>0.11.2</jjwt.version>
 	<jna.version>5.13.0</jna.version>
@@ -121,7 +121,7 @@
 	<spring-boot.version>3.0.5</spring-boot.version>
 	<spring-security.version>6.0.2</spring-security.version>
 	<swagger.version>2.2.7</swagger.version>
-	<type-parser.version>0.7.0</type-parser.version>
+	<type-parser.version>0.8.1</type-parser.version>
 	<wildfly-common.version>1.6.0.Final</wildfly-common.version>
 	<hawtbuf.version>1.11</hawtbuf.version>
 	<netty-tc-native.version>2.0.52.Final</netty-tc-native.version>
diff --git a/streampipes-client-python/setup.py b/streampipes-client-python/setup.py
index 14fb16a176..5602360afe 100644
--- a/streampipes-client-python/setup.py
+++ b/streampipes-client-python/setup.py
@@ -67,7 +67,7 @@ docs_packages = [
     "mkdocs-material==9.1.3",
     "mkdocstrings[python]==0.21.1",
     "pytkdocs[numpy-style]>=0.16.1",
-    "mkdocs-gen-files==0.4.0",
+    "mkdocs-gen-files==0.5.0",
     "mkdocs-literate-nav==0.6.0",
     "numpydoc==1.5.0",
     "mkdocs-jupyter==0.24.0",
diff --git a/streampipes-client/pom.xml b/streampipes-client/pom.xml
index f24e663780..25c96b1a12 100644
--- a/streampipes-client/pom.xml
+++ b/streampipes-client/pom.xml
@@ -97,8 +97,13 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    <propertyExpansion>
+                		checkstyle.config.base.path=${project.parent.basedir}/tools/maven
+                	</propertyExpansion>
+                </configuration>
             </plugin>
         </plugins>
     </build>
 
-</project>
+</project>
\ No newline at end of file
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/AbstractTypedClientApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/AbstractTypedClientApi.java
index abd60c439a..ff3964bd8e 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/AbstractTypedClientApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/AbstractTypedClientApi.java
@@ -22,9 +22,11 @@ import org.apache.streampipes.client.model.StreamPipesClientConfig;
 import org.apache.streampipes.client.serializer.ListSerializer;
 import org.apache.streampipes.client.serializer.ObjectSerializer;
 import org.apache.streampipes.client.util.StreamPipesApiPath;
+import org.apache.streampipes.commons.exceptions.SpHttpErrorStatusCode;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 
 import java.util.List;
+import java.util.Optional;
 
 public abstract class AbstractTypedClientApi<T> extends AbstractClientApi {
 
@@ -40,9 +42,17 @@ public abstract class AbstractTypedClientApi<T> extends AbstractClientApi {
     return new GetRequest<>(clientConfig, apiPath, targetClass, serializer).executeRequest();
   }
 
-  protected T getSingle(StreamPipesApiPath apiPath) throws SpRuntimeException {
+  protected Optional<T> getSingle(StreamPipesApiPath apiPath) throws SpRuntimeException {
     ObjectSerializer<Void, T> serializer = new ObjectSerializer<>();
-    return new GetRequest<>(clientConfig, apiPath, targetClass, serializer).executeRequest();
+    try {
+      return Optional.of(new GetRequest<>(clientConfig, apiPath, targetClass, serializer).executeRequest());
+    } catch (SpHttpErrorStatusCode e) {
+      if (e.getHttpStatusCode() == 404) {
+        return Optional.empty();
+      } else {
+        throw e;
+      }
+    }
   }
 
   protected abstract StreamPipesApiPath getBaseResourcePath();
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/CRUDApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/CRUDApi.java
index 6cabcc09ab..cbe4221850 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/CRUDApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/CRUDApi.java
@@ -18,10 +18,11 @@
 package org.apache.streampipes.client.api;
 
 import java.util.List;
+import java.util.Optional;
 
 public interface CRUDApi<K, V> {
 
-  V get(K id);
+  Optional<V> get(K id);
 
   List<V> all();
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataLakeMeasureApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataLakeMeasureApi.java
index d59b4898ea..702723ad44 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataLakeMeasureApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataLakeMeasureApi.java
@@ -23,6 +23,7 @@ import org.apache.streampipes.client.util.StreamPipesApiPath;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 
 import java.util.List;
+import java.util.Optional;
 
 public class DataLakeMeasureApi extends AbstractTypedClientApi<DataLakeMeasure>
     implements CRUDApi<String, DataLakeMeasure> {
@@ -31,7 +32,7 @@ public class DataLakeMeasureApi extends AbstractTypedClientApi<DataLakeMeasure>
     super(clientConfig, DataLakeMeasure.class);
   }
 
-  public DataLakeMeasure get(String id) {
+  public Optional<DataLakeMeasure> get(String id) {
     return getSingle(getBaseResourcePath().addToPath(id));
   }
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataProcessorApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataProcessorApi.java
index 656d5cf185..64f7ea7ee4 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataProcessorApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataProcessorApi.java
@@ -28,6 +28,7 @@ import org.apache.streampipes.messaging.kafka.SpKafkaConsumer;
 import org.apache.streampipes.model.graph.DataProcessorInvocation;
 
 import java.util.List;
+import java.util.Optional;
 
 public class DataProcessorApi extends AbstractTypedClientApi<DataProcessorInvocation>
     implements CRUDApi<String, DataProcessorInvocation> {
@@ -43,7 +44,7 @@ public class DataProcessorApi extends AbstractTypedClientApi<DataProcessorInvoca
   }
 
   @Override
-  public DataProcessorInvocation get(String s) {
+  public Optional<DataProcessorInvocation> get(String s) {
     return getSingle(getBaseResourcePath().addToPath(s));
   }
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataSinkApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataSinkApi.java
index d53d21047a..694d458d62 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataSinkApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataSinkApi.java
@@ -27,6 +27,7 @@ import org.apache.streampipes.messaging.kafka.SpKafkaConsumer;
 import org.apache.streampipes.model.graph.DataSinkInvocation;
 
 import java.util.List;
+import java.util.Optional;
 
 public class DataSinkApi extends AbstractTypedClientApi<DataSinkInvocation>
     implements CRUDApi<String, DataSinkInvocation> {
@@ -36,7 +37,7 @@ public class DataSinkApi extends AbstractTypedClientApi<DataSinkInvocation>
   }
 
   @Override
-  public DataSinkInvocation get(String s) {
+  public Optional<DataSinkInvocation> get(String s) {
     return getSingle(getBaseResourcePath().addToPath(s));
   }
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataStreamApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataStreamApi.java
index d3b8d28428..dd0c3e8247 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataStreamApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/DataStreamApi.java
@@ -28,6 +28,7 @@ import org.apache.streampipes.model.message.Message;
 
 import java.net.URLEncoder;
 import java.util.List;
+import java.util.Optional;
 
 public class DataStreamApi extends AbstractTypedClientApi<SpDataStream> implements CRUDApi<String, SpDataStream> {
 
@@ -36,7 +37,7 @@ public class DataStreamApi extends AbstractTypedClientApi<SpDataStream> implemen
   }
 
   @Override
-  public SpDataStream get(String streamId) {
+  public Optional<SpDataStream> get(String streamId) {
     return getSingle(StreamPipesApiPath.fromBaseApiPath()
         .addToPath("streams").addToPath(streamId));
   }
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineApi.java
index 888c15df79..8cc180057e 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineApi.java
@@ -26,6 +26,7 @@ import org.apache.streampipes.model.pipeline.Pipeline;
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
 
 import java.util.List;
+import java.util.Optional;
 
 public class PipelineApi extends AbstractTypedClientApi<Pipeline> implements CRUDApi<String, Pipeline> {
 
@@ -34,7 +35,7 @@ public class PipelineApi extends AbstractTypedClientApi<Pipeline> implements CRU
   }
 
   @Override
-  public Pipeline get(String pipelineId) {
+  public Optional<Pipeline> get(String pipelineId) {
     return getSingle(getBaseResourcePath().addToPath(pipelineId));
   }
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineElementTemplateApi.java b/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineElementTemplateApi.java
index 3038fefdde..33c9c22c53 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineElementTemplateApi.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/api/PipelineElementTemplateApi.java
@@ -22,6 +22,7 @@ import org.apache.streampipes.client.util.StreamPipesApiPath;
 import org.apache.streampipes.model.template.PipelineElementTemplate;
 
 import java.util.List;
+import java.util.Optional;
 
 public class PipelineElementTemplateApi extends AbstractTypedClientApi<PipelineElementTemplate>
     implements CRUDApi<String, PipelineElementTemplate> {
@@ -31,7 +32,7 @@ public class PipelineElementTemplateApi extends AbstractTypedClientApi<PipelineE
   }
 
   @Override
-  public PipelineElementTemplate get(String id) {
+  public Optional<PipelineElementTemplate> get(String id) {
     return getSingle(getBaseResourcePath().addToPath(id));
   }
 
diff --git a/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java b/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
index b0ffc92ad5..e34a1225ba 100644
--- a/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
+++ b/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
@@ -22,6 +22,7 @@ import org.apache.streampipes.client.model.ClientConnectionUrlResolver;
 import org.apache.streampipes.client.model.StreamPipesClientConfig;
 import org.apache.streampipes.client.serializer.Serializer;
 import org.apache.streampipes.client.util.StreamPipesApiPath;
+import org.apache.streampipes.commons.exceptions.SpHttpErrorStatusCode;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -100,14 +101,19 @@ public abstract class HttpRequest<K, V, T> {
       if (status.getStatusCode() == HttpStatus.SC_OK) {
         return afterRequest(serializer, response.getEntity());
       } else {
-        if (status.getStatusCode() == 401) {
-          throw new SpRuntimeException(
-              " 401 - Access to this resource is forbidden - did you provide a poper API key or client secret?");
-        } else {
-          throw new SpRuntimeException(status.getStatusCode() + " - " + status.getReasonPhrase());
+        switch (status.getStatusCode()) {
+          case HttpStatus.SC_UNAUTHORIZED:
+            throw new SpHttpErrorStatusCode(
+                " 401 - Access to this resource is forbidden - did you provide a poper API key or client secret?",
+                401);
+          case HttpStatus.SC_NOT_FOUND:
+            throw new SpHttpErrorStatusCode(" 404 - The requested resource could not be found.", 404);
+          default:
+            throw new SpHttpErrorStatusCode(status.getStatusCode() + " - " + status.getReasonPhrase(),
+                status.getStatusCode());
         }
       }
-    } catch (IOException | SpRuntimeException e) {
+    } catch (IOException e) {
       throw new SpRuntimeException(
           "Could not connect to the StreamPipes API - please check that StreamPipes is available", e);
     }
diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpHttpErrorStatusCode.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpHttpErrorStatusCode.java
new file mode 100644
index 0000000000..a4188e9664
--- /dev/null
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpHttpErrorStatusCode.java
@@ -0,0 +1,39 @@
+/*
+ * 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.commons.exceptions;
+
+public class SpHttpErrorStatusCode extends SpRuntimeException {
+
+  private static final long serialVersionUID = 2289470758226670270L;
+  private Integer httpStatusCode;
+
+  /**
+   * Creates a new Exception with the given message and null as the cause.
+   *
+   * @param message The exception message
+   */
+  public SpHttpErrorStatusCode(String message, Integer httpStatusCode) {
+    super(message);
+    this.httpStatusCode = httpStatusCode;
+  }
+
+  public Integer getHttpStatusCode() {
+    return httpStatusCode;
+  }
+}
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/streampipes-rest-extensions/pom.xml b/streampipes-rest-extensions/pom.xml
index d95d3e45de..192260917d 100644
--- a/streampipes-rest-extensions/pom.xml
+++ b/streampipes-rest-extensions/pom.xml
@@ -75,4 +75,4 @@
         </plugins>
     </build>
 
-</project>
+</project>
\ No newline at end of file
diff --git a/streampipes-rest-shared/pom.xml b/streampipes-rest-shared/pom.xml
index de74e640dc..e94975373e 100644
--- a/streampipes-rest-shared/pom.xml
+++ b/streampipes-rest-shared/pom.xml
@@ -17,7 +17,8 @@
   ~
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
         <artifactId>streampipes-parent</artifactId>
         <groupId>org.apache.streampipes</groupId>
diff --git a/streampipes-rest-shared/src/main/java/org/apache/streampipes/rest/shared/impl/AbstractSharedRestInterface.java b/streampipes-rest-shared/src/main/java/org/apache/streampipes/rest/shared/impl/AbstractSharedRestInterface.java
index 4d5b78a9c1..74897e29b0 100644
--- a/streampipes-rest-shared/src/main/java/org/apache/streampipes/rest/shared/impl/AbstractSharedRestInterface.java
+++ b/streampipes-rest-shared/src/main/java/org/apache/streampipes/rest/shared/impl/AbstractSharedRestInterface.java
@@ -31,12 +31,15 @@ public abstract class AbstractSharedRestInterface {
     return error(entity, 400);
   }
 
+  protected <T> Response notFound(T entity) {
+    return error(entity, 404);
+  }
+
   protected <T> Response serverError(T entity) {
     return error(entity, 500);
   }
 
-  protected <T> Response error(T entity,
-                               Integer statusCode) {
+  protected <T> Response error(T entity, Integer statusCode) {
     return Response
         .status(statusCode)
         .entity(entity)
diff --git a/streampipes-rest/pom.xml b/streampipes-rest/pom.xml
index 64eb418d7c..94bcca3803 100644
--- a/streampipes-rest/pom.xml
+++ b/streampipes-rest/pom.xml
@@ -159,7 +159,12 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    <propertyExpansion>
+                        checkstyle.config.base.path=${project.parent.basedir}/tools/maven
+                    </propertyExpansion>
+                </configuration>
             </plugin>
         </plugins>
     </build>
-</project>
+</project>
\ No newline at end of file
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
index eafa7179d3..b71064f3c7 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
@@ -18,7 +18,6 @@
 
 package org.apache.streampipes.rest.impl;
 
-
 import org.apache.streampipes.commons.exceptions.NoMatchingFormatException;
 import org.apache.streampipes.commons.exceptions.NoMatchingJsonSchemaException;
 import org.apache.streampipes.commons.exceptions.NoMatchingProtocolException;
@@ -76,14 +75,12 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Get all pipelines of the current user",
-      tags = {"Pipeline"},
-      responses = {
-          @ApiResponse(content = {
-              @Content(
+  @Operation(summary = "Get all pipelines of the current user", tags = {"Pipeline"}, responses = {
+      @ApiResponse(content = {
+          @Content(
                   mediaType = "application/json",
-                  array = @ArraySchema(schema = @Schema(implementation = Pipeline.class)))
-          })})
+                  array = @ArraySchema(schema = @Schema(implementation = Pipeline.class))
+          )})})
   @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
   @PostFilter("hasPermission(filterObject.pipelineId, 'READ')")
   public List<Pipeline> get() {
@@ -94,8 +91,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Produces(MediaType.APPLICATION_JSON)
   @Path("/{pipelineId}/status")
   @JacksonSerialized
-  @Operation(summary = "Get the pipeline status of a given pipeline",
-      tags = {"Pipeline"})
+  @Operation(summary = "Get the pipeline status of a given pipeline", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
   public Response getPipelineStatus(@PathParam("pipelineId") String pipelineId) {
     return ok(PipelineStatusManager.getPipelineStatus(pipelineId, 5));
@@ -105,8 +101,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Path("/{pipelineId}")
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Delete a pipeline with a given id",
-      tags = {"Pipeline"})
+  @Operation(summary = "Delete a pipeline with a given id", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_DELETE_PIPELINE_PRIVILEGE)
   public Response removeOwn(@PathParam("pipelineId") String pipelineId) {
     PipelineManager.deletePipeline(pipelineId);
@@ -117,19 +112,23 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Path("/{pipelineId}")
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Get a specific pipeline with the given id",
-      tags = {"Pipeline"})
+  @Operation(summary = "Get a specific pipeline with the given id", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
   public Response getElement(@PathParam("pipelineId") String pipelineId) {
-    return ok(PipelineManager.getPipeline(pipelineId));
+    Pipeline foundPipeline = PipelineManager.getPipeline(pipelineId);
+
+    if (foundPipeline == null) {
+      return notFound("Pipeline with " + pipelineId + " not found.");
+    } else {
+      return ok(PipelineManager.getPipeline(pipelineId));
+    }
   }
 
   @Path("/{pipelineId}/start")
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Start the pipeline with the given id",
-      tags = {"Pipeline"})
+  @Operation(summary = "Start the pipeline with the given id", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
   public Response start(@PathParam("pipelineId") String pipelineId) {
     try {
@@ -146,28 +145,24 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Stop the pipeline with the given id",
-      tags = {"Pipeline"})
+  @Operation(summary = "Stop the pipeline with the given id", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
   public Response stop(@PathParam("pipelineId") String pipelineId,
-                       @QueryParam("forceStop") @DefaultValue("false") boolean forceStop) {
+             @QueryParam("forceStop") @DefaultValue("false") boolean forceStop) {
     try {
       PipelineOperationStatus status = PipelineManager.stopPipeline(pipelineId, forceStop);
       return ok(status);
-    } catch
-    (Exception e) {
+    } catch (Exception e) {
       e.printStackTrace();
-      return constructErrorMessage(
-          new Notification(NotificationType.UNKNOWN_ERROR.title(), NotificationType.UNKNOWN_ERROR.description(),
-              e.getMessage()));
+      return constructErrorMessage(new Notification(NotificationType.UNKNOWN_ERROR.title(),
+          NotificationType.UNKNOWN_ERROR.description(), e.getMessage()));
     }
   }
 
   @POST
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Store a new pipeline",
-      tags = {"Pipeline"})
+  @Operation(summary = "Store a new pipeline", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
   public Response addPipeline(Pipeline pipeline) {
 
@@ -184,21 +179,17 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Hidden
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
   @PostAuthorize("hasPermission(returnObject, 'READ')")
-  public PipelineElementRecommendationMessage recommend(Pipeline pipeline,
-                                                        @PathParam("recId") String baseRecElement) {
+  public PipelineElementRecommendationMessage recommend(Pipeline pipeline, @PathParam("recId") String baseRecElement) {
     try {
       return Operations.findRecommendedElements(pipeline, baseRecElement);
     } catch (JsonSyntaxException e) {
-      throw new WebApplicationException(badRequest(new Notification(NotificationType.UNKNOWN_ERROR,
-          e.getMessage())));
+      throw new WebApplicationException(badRequest(new Notification(NotificationType.UNKNOWN_ERROR, e.getMessage())));
     } catch (NoSuitableSepasAvailableException e) {
-      throw new WebApplicationException(badRequest(new Notification(NotificationType.NO_SEPA_FOUND,
-          e.getMessage())));
+      throw new WebApplicationException(badRequest(new Notification(NotificationType.NO_SEPA_FOUND, e.getMessage())));
     } catch (Exception e) {
       e.printStackTrace();
       throw new WebApplicationException(
-          serverError(constructErrorMessage(new Notification(NotificationType.UNKNOWN_ERROR,
-              e.getMessage()))));
+          serverError(constructErrorMessage(new Notification(NotificationType.UNKNOWN_ERROR, e.getMessage()))));
     }
   }
 
@@ -224,26 +215,20 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
     try {
       return ok(Operations.validatePipeline(pipeline));
     } catch (JsonSyntaxException e) {
-      return badRequest(new Notification(NotificationType.UNKNOWN_ERROR,
-          e.getMessage()));
+      return badRequest(new Notification(NotificationType.UNKNOWN_ERROR, e.getMessage()));
     } catch (NoMatchingSchemaException e) {
-      return badRequest(new Notification(NotificationType.NO_VALID_CONNECTION,
-          e.getMessage()));
+      return badRequest(new Notification(NotificationType.NO_VALID_CONNECTION, e.getMessage()));
     } catch (NoMatchingFormatException e) {
-      return badRequest(new Notification(NotificationType.NO_MATCHING_FORMAT_CONNECTION,
-          e.getMessage()));
+      return badRequest(new Notification(NotificationType.NO_MATCHING_FORMAT_CONNECTION, e.getMessage()));
     } catch (NoMatchingProtocolException e) {
-      return badRequest(new Notification(NotificationType.NO_MATCHING_PROTOCOL_CONNECTION,
-          e.getMessage()));
+      return badRequest(new Notification(NotificationType.NO_MATCHING_PROTOCOL_CONNECTION, e.getMessage()));
     } catch (RemoteServerNotAccessibleException | NoMatchingJsonSchemaException e) {
-      return serverError(new Notification(NotificationType.REMOTE_SERVER_NOT_ACCESSIBLE
-          , e.getMessage()));
+      return serverError(new Notification(NotificationType.REMOTE_SERVER_NOT_ACCESSIBLE, e.getMessage()));
     } catch (InvalidConnectionException e) {
       return badRequest(e.getErrorLog());
     } catch (Exception e) {
       e.printStackTrace();
-      return serverError(new Notification(NotificationType.UNKNOWN_ERROR,
-          e.getMessage()));
+      return serverError(new Notification(NotificationType.UNKNOWN_ERROR, e.getMessage()));
     }
   }
 
@@ -251,11 +236,9 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Path("/{pipelineId}")
   @Produces(MediaType.APPLICATION_JSON)
   @JacksonSerialized
-  @Operation(summary = "Update an existing pipeline",
-      tags = {"Pipeline"})
+  @Operation(summary = "Update an existing pipeline", tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
-  public Response overwritePipeline(@PathParam("pipelineId") String pipelineId,
-                                    Pipeline pipeline) {
+  public Response overwritePipeline(@PathParam("pipelineId") String pipelineId, Pipeline pipeline) {
     Pipeline storedPipeline = getPipelineStorage().getPipeline(pipelineId);
     if (!storedPipeline.isRunning()) {
       storedPipeline.setStreams(pipeline.getStreams());
@@ -275,14 +258,10 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
   @Produces(MediaType.APPLICATION_JSON)
   @Path("/contains/{elementId}")
   @JacksonSerialized
-  @Operation(summary = "Returns all pipelines that contain the element with the elementId",
-      tags = {"Pipeline"},
-      responses = {
-          @ApiResponse(content = {
-              @Content(
-                  mediaType = "application/json",
-                  array = @ArraySchema(schema = @Schema(implementation = Pipeline.class)))
-          })})
+  @Operation(summary = "Returns all pipelines that contain the element with the elementId", tags = {
+      "Pipeline"}, responses = {@ApiResponse(content = {
+        @Content(mediaType = "application/json",
+          array = @ArraySchema(schema = @Schema(implementation = Pipeline.class)))})})
   @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
   @PostFilter("hasPermission(filterObject.pipelineId, 'READ')")
   public List<Pipeline> getPipelinesContainingElement(@PathParam("elementId") String elementId) {
diff --git a/streampipes-wrapper-standalone/src/main/java/org/apache/streampipes/wrapper/standalone/function/FunctionContextGenerator.java b/streampipes-wrapper-standalone/src/main/java/org/apache/streampipes/wrapper/standalone/function/FunctionContextGenerator.java
index c3465b4138..9b56d0d078 100644
--- a/streampipes-wrapper-standalone/src/main/java/org/apache/streampipes/wrapper/standalone/function/FunctionContextGenerator.java
+++ b/streampipes-wrapper-standalone/src/main/java/org/apache/streampipes/wrapper/standalone/function/FunctionContextGenerator.java
@@ -25,7 +25,7 @@ import org.apache.streampipes.wrapper.routing.SpOutputCollector;
 
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.Optional;
 
 public class FunctionContextGenerator {
 
@@ -60,6 +60,7 @@ public class FunctionContextGenerator {
     return this.streamIds
         .stream()
         .map(streamId -> client.streams().get(streamId))
-        .collect(Collectors.toList());
+        .flatMap(Optional::stream)
+        .toList();
   }
 }
diff --git a/tools/maven/checkstyle.xml b/tools/maven/checkstyle.xml
index 83861dbe07..a86ef413dc 100644
--- a/tools/maven/checkstyle.xml
+++ b/tools/maven/checkstyle.xml
@@ -28,7 +28,7 @@ page at http://checkstyle.sourceforge.net/config.html -->
     <property name="severity" value="error"/>
 
     <module name="Header">
-        <property name="headerFile" value="tools/maven/checkstyle-header.txt"/>
+        <property name="headerFile" value="${checkstyle.config.base.path}/checkstyle-header.txt" default="tools/maven/checkstyle-header.txt"/>
         <property name="fileExtensions" value="java"/>
     </module>
 
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/projects/streampipes/platform-services/package.json b/ui/projects/streampipes/platform-services/package.json
index 9cb36e4211..3ababad319 100644
--- a/ui/projects/streampipes/platform-services/package.json
+++ b/ui/projects/streampipes/platform-services/package.json
@@ -12,7 +12,7 @@
   },
   "devDependencies": {
     "karma": "~6.4.1",
-    "karma-chrome-launcher": "~3.1.0",
+    "karma-chrome-launcher": "~3.2.0",
     "karma-coverage-istanbul-reporter": "~3.0.2",
     "karma-firefox-launcher": "^2.1.2",
     "karma-jasmine": "~5.1.0",
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss
index 66eb0f253d..6341039951 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-view/basic-view.component.scss
@@ -42,7 +42,7 @@
 .page-container {
     margin: 10px;
     border: 1px solid var(--color-bg-3);
-    min-height: calc(100% - 50px);
+    min-height: calc(100vh - 90px);
 }
 
 .page-container-padding-inner {
diff --git a/ui/src/app/connect/dialog/edit-event-property/components/edit-schema-transformation/edit-schema-transformation.component.html b/ui/src/app/connect/dialog/edit-event-property/components/edit-schema-transformation/edit-schema-transformation.component.html
index ceb0765434..8ca237642b 100644
--- a/ui/src/app/connect/dialog/edit-event-property/components/edit-schema-transformation/edit-schema-transformation.component.html
+++ b/ui/src/app/connect/dialog/edit-event-property/components/edit-schema-transformation/edit-schema-transformation.component.html
@@ -72,11 +72,12 @@
                     [matAutocompleteDisabled]="isTimestampProperty"
                     [formControl]="domainPropertyControl"
                 />
-                <mat-autocomplete #st="matAutocomplete" [panelWidth]="'300px'">
+                <mat-autocomplete #st="matAutocomplete" [panelWidth]="'400px'">
                     <mat-option
                         *ngFor="let semanticType of semanticTypes | async"
                         [value]="semanticType"
                         style="font-size: 10pt"
+                        [matTooltip]="semanticType"
                     >
                         {{ semanticType }}
                     </mat-option>
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));
+  }
+}