Repository: apache/streampipes
Pull request #2196 (2023-11-17 10:08:28)

fix(#2106) Serialize non-primitive types as string and store in InfluxDB
<!--
  ~ 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.
-->
See #2106 

### 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

I treated ListField and NestedField as the same and used Jackson to serialize them into string.

For this test adapter: ![9a64bd488ff1a0cc8cd422783dc4e13](https://github.com/apache/streampipes/assets/76769570/7a038e6e-e646-4689-bcd5-8413b814b3fe)
Now we can see the raw value being stored (but not other visualizations such as table because they are not implemented yet): 
![cf10c485fdb20dbc6ea6285610d1ddd](https://github.com/apache/streampipes/assets/76769570/8b44a8ed-c3ea-4071-8a8b-815c9fe9e9a6)
The raw value doesn't show deserialization result, so I have left some commented out codes that do the deserialization and below shows that the original types are kept:
![808b41dab8710524cff2b57aca2f930](https://github.com/apache/streampipes/assets/76769570/7dc0a841-263a-4d25-b611-3a02e29ea934)

### **HOWEVER**
I am a little skeptical about my approach so please let me know if this is okay. I am somewhat confused on the difference between serializing Abstract/Primitive/List/Nested Field vs. serializing raw values. The reasons I chose to serialize raw values are:

- Serializing Abstract/Primitive/List/Nested Field which includes inheritance is very hard, I have found multiple resources online but no luck to get the correct result.
- Later I found out the serialization/deserialization of those Fields are unnecessary because InfluxDB is actually storing runtime name and raw value.

Let me know if it is necessary to serialize those Fields. Currently I don't think so because during deserialization, we can check `instanceof List` and `instanceof Map` for `Object`, just like we use `instanceof ListField` and `instanceof NestedField` for `AbstractField`.


Patch:
diff --git a/installer/k8s/README.md b/installer/k8s/README.md
index adeca3ea09..d55f521053 100644
--- a/installer/k8s/README.md
+++ b/installer/k8s/README.md
@@ -15,7 +15,9 @@
   ~ limitations under the License.
   ~
   -->
+
 # StreamPipes k8s - The Operator's Dream
+
 StreamPipes k8s is a helm chart to deploy StreamPipes on Kubernetes.
 
 <!-- BEGIN do not edit: set via ../upgrade_versions.sh -->
@@ -28,20 +30,25 @@ We provide two helm chart templates to get you going:
 - **full**:  contains more pipeline elements, requires **>16 GB RAM** (recommended)
 
 ## Prerequisite
+
 Requires Helm (https://helm.sh/) and an active connection to a kubernetes cluster with a running tiller server.
 
 Tested with:
+
 * K8s v1.19.3
 * Helm v3.1.2
 * Minikube v1.15.1 (recommended for local testing)
 
-> **NOTE**: We experienced some problems with using host path volumes in Docker Desktop environments for persistent storage. Therefore, we suggest to use minikube for local testing.
+> **NOTE**: We experienced some problems with using host path volumes in Docker Desktop environments for persistent
+> storage. Therefore, we suggest to use minikube for local testing.
 
 ## Local testing
 
-We recommend using [minikube](https://minikube.sigs.k8s.io/docs/) for local testing. Follow instructions in their docs to setup test environment
+We recommend using [minikube](https://minikube.sigs.k8s.io/docs/) for local testing. Follow instructions in their docs
+to setup test environment
 
 Once installed, start local minikube node with a mapped host volume:
+
 ```bash
 minikube start --mount-string ${HOME}/streampipes-k8s:/streampipes-k8s --mount --memory=4g --cpus=4
 ```
@@ -55,7 +62,9 @@ helm install streampipes ./
 # full template only recommend if you have sufficient resources
 # helm install streampipes ./ --set deployment=full
 ```
+
 After a while, all containers should successfully started, indicated by the `Running` status.
+
 ```bash
 kubectl get pods
 NAME                                           READY   STATUS    RESTARTS   AGE
@@ -68,239 +77,256 @@ ui-b94bd9766-rm6zb                             2/2     Running   0          3m27
 ```
 
 For **minikube users**:
-> **NOTE**: If you're running Docker Desktop or Minikube with a local k8s cluster, the above step to use your host IP might not work. Luckily, you can port-forward a service port to your localhost using the following command to be able to access the UI either via `http://localhost` or `http://<HOST_IP>` (you require sudo to run this command in order to bind to a privileged port).
+> **NOTE**: If you're running Docker Desktop or Minikube with a local k8s cluster, the above step to use your host IP
+> might not work. Luckily, you can port-forward a service port to your localhost using the following command to be able to
+> access the UI either via `http://localhost` or `http://<HOST_IP>` (you require sudo to run this command in order to bind
+> to a privileged port).
+
 ```bash
 kubectl port-forward svc/ui --address=0.0.0.0 80:80
 ```
 
 **Deleting** the current helm chart deployment:
+
 ```bash
 helm del streampipes
 ```
 
 We retain the created persistent volume. You need to manually delete it:
+
 ```bash
 rm -rf ${HOME}/streampipes-k8s
 ```
-##Parameters
-
-###Common parameters
-| Parameter Name                                   | Description                                             | Value                                   |
-|--------------------------------------------------|---------------------------------------------------------|-----------------------------------------|
-| deployment                                       | Deployment type (lite or full)                          | lite                                    |
-| preferredBroker                                  | Preferred broker for deployment                         | "nats"                                  |
-| monitoringSystem                                 | Enable monitoring system (true/false)                   | false                                   |
-| pullPolicy                                       | Image pull policy                                       | "Always"                                |
-| restartPolicy                                    | Restart policy for the container                        | Always                                  |
-| persistentVolumeReclaimPolicy                    | Reclaim policy for persistent volumes                   | "Delete"                                |
-| persistentVolumeAccessModes                      | Access mode for persistent volumes                      | "ReadWriteOnce"                         |
-| initialDelaySeconds                              | Initial delay for liveness and readiness probes         | 60                                      |
-| periodSeconds                                    | Interval between liveness and readiness probes          | 30                                      |
-| failureThreshold                                 | Number of consecutive failures for readiness probes     | 30                                      |
-| hostPath                                         | Host path for the application                           | ""                                      |
-
-###StreamPipes common parameters
-| Parameter Name                                  | Description                                             | Value                                    |
-|-------------------------------------------------|---------------------------------------------------------|------------------------------------------|
-| streampipes.version                             | StreamPipes version                                     | "0.93.0-SNAPSHOT"                        |
-| streampipes.registry                            | StreamPipes registry URL                                | "apachestreampipes"                      |
-| streampipes.auth.secretName                     | The secret name for storing secrets                     | "sp-secrets"                             |
-| streampipes.auth.users.admin.user               | The initial admin user                                  | "admin@streampipes.apache.org"           |
-| streampipes.auth.users.admin.password           | The initial admin password (leave empty for autogen)    | "admin"                                  |
-| streampipes.auth.users.service.user             | The initial service account user                        | "sp-service-client"                      |
-| streampipes.auth.users.service.secret           | The initial service account secret                      | empty (auto-generated)                   |
-| streampipes.auth.encryption.passcode            | Passcode for value encryption                           | empty (auto-generated)                   |
-| streampipes.core.appName                        | StreamPipes backend application name                    | "backend"                                |
-| streampipes.core.port                           | StreamPipes backend port                                | 8030                                     |
-| streampipes.core.persistence.storageClassName   | Storage class name for backend PVs                      | "hostpath"                               |
-| streampipes.core.persistence.storageSize        | Size of the backend PV                                  | "1Gi"                                    |
-| streampipes.core.persistence.claimName          | Name of the backend PersistentVolumeClaim               | "backend-pvc"                            |
-| streampipes.core.persistence.pvName             | Name of the backend PersistentVolume                    | "backend-pv"                             |
-| streampipes.core.service.name                   | Name of the backend service                             | "backend"                                |
-| streampipes.core.service.port                   | TargetPort of the StreamPipes backend service           | 8030                                     |
-| streampipes.ui.appName                          | StreamPipes UI application name                         | "ui"                                     |
-| streampipes.ui.resolverActive                   | Flag for enabling DNS resolver for Nginx proxy          | true                                     |
-| streampipes.ui.port                             | StreamPipes UI port                                     | 8088                                     |
-| streampipes.ui.resolver                         | DNS resolver for Nginx proxy                            | "kube-dns.kube-system.svc.cluster.local" |
-| streampipes.ui.service.name                     | Name of the UI service                                  | "ui"                                     |
-| streampipes.ui.service.type                     | Type of the UI service                                  | "ClusterIP"                              |
-| streampipes.ui.service.nodePort                 | Node port for the UI service                            | 8088                                     |
-| streampipes.ui.service.port                     | TargetPort of the StreamPipes UI service                | 8088                                     |
-| streampipes.ingress.active                      | Flag for enabling Ingress for StreamPipes               | false                                    |
-| streampipes.ingress.annotations                 | Annotations for Ingress                                 | {}                                       |
-| streampipes.ingress.host                        | Hostname for Ingress                                    | ""                                       |
-| streampipes.ingressroute.active                 | Flag for enabling IngressRoute for StreamPipes          | true                                     |
-| streampipes.ingressroute.annotations            | Annotations for IngressRoute                            | {}                                       |
-| streampipes.ingressroute.entryPoints            | Entry points for IngressRoute                           | ["web", "websecure"]                     |
-| streampipes.ingressroute.host                   | Hostname for IngressRoute                               | ""                                       |
-| streampipes.ingressroute.certResolverActive     | Flag for enabling certificate resolver for IngressRoute | true                                     |
-| streampipes.ingressroute.certResolver           | Certificate resolver for IngressRoute                   | ""                                       |
-
-
-###Extensions common parameters
-| Parameter Name                                  | Description                                             | Value                                    |
-|-------------------------------------------------|---------------------------------------------------------|------------------------------------------|
-| extensions.iiot.appName                         | IIoT extensions application name                        | extensions-all-iiot                      |
-| extensions.iiot.port                            | Port for the IIoT extensions application                | 8090                                     |
-| extensions.iiot.service.name                    | Name of the IIoT extensions service                     | extensions-all-iiot                      |
-| extensions.iiot.service.port                    | TargetPort of the IIoT extensions service               | 8090                                     |
-
-
-###External common parameters
-
-####Consul common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.consul.appName                         | Consul application name                                  | "consul"                                 |
-| external.consul.version                         | Consul version                                           | 1.14.3                                   |
-| external.consul.webPort                         | Port number for the Consul web interface                 | 8500                                     |
-| external.consul.dnsPort                         | Port number for the Consul DNS interface                 | 8600                                     |
-| external.consul.persistence.storageClassName    | Storage class name for Consul PVs                        | "hostpath"                               |
-| external.consul.persistence.storageSize         | Size of the Consul PV                                    | "1Gi"                                    |
-| external.consul.persistence.claimName           | Name of the Consul PersistentVolumeClaim                 | "consul-pvc"                             |
-| external.consul.persistence.pvName              | Name of the Consul PersistentVolume                      | "consul-pv"                              |
-| external.consul.service.name                    | Name of the Consul service                               | "consul"                                 |
-| external.consul.service.webPort                 | TargetPort of the Consul service for web interface       | 8500                                     |
-| external.consul.service.dnsPort                 | TargetPort of the Consul service for DNS interface       | 8600                                     |
-
-####Couchdb common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.couchdb.appName                        | CouchDB application name                                 | "couchdb"                                |
-| external.couchdb.version                        | CouchDB version                                          | 3.3.1                                    |
-| external.couchdb.user                           | CouchDB admin username                                   | "admin"                                  |
-| external.couchdb.password                       | CouchDB admin password                                   | empty (auto-generated)                   |
-| external.couchdb.port                           | Port for the CouchDB service                             | 5984                                     |
-| external.couchdb.service.name                   | Name of the CouchDB service                              | "couchdb"                                |
-| external.couchdb.service.port                   | TargetPort of the CouchDB service                        | 5984                                     |
-| external.couchdb.persistence.storageClassName   | Storage class name for CouchDB PVs                       | "hostpath"                               |
-| external.couchdb.persistence.storageSize        | Size of the CouchDB PV                                   | "1Gi"                                    |
-| external.couchdb.persistence.claimName          | Name of the CouchDB PersistentVolumeClaim                | "couchdb-pvc"                            |
-| external.couchdb.persistence.pvName             | Name of the CouchDB PersistentVolume                     | "couchdb-pv"                             |
-
-####Influxdb common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.influxdb.appName                       | InfluxDB application name                                | "influxdb"                               |
-| external.influxdb.version                       | InfluxDB version                                         | 2.6                                      |
-| external.influxdb.username                      | InfluxDB admin username                                  | "admin"                                  |
-| external.influxdb.password                      | InfluxDB admin password                                  | empty (auto-generated)                   |
-| external.influxdb.adminToken                    | InfluxDB admin token                                     | empty (auto-generated)                   |
-| external.influxdb.initOrg                       | InfluxDB initial organization                            | "sp"                                     |
-| external.influxdb.initBucket                    | InfluxDB initial bucket                                  | "sp"                                     |
-| external.influxdb.initMode                      | InfluxDB initialization mode                             | "setup"                                  |
-| external.influxdb.apiPort                       | Port number for the InfluxDB service (API)               | 8083                                     |
-| external.influxdb.httpPort                      | Port number for the InfluxDB service (HTTP)              | 8086                                     |
-| external.influxdb.grpcPort                      | Port number for the InfluxDB service (gRPC)              | 8090                                     |
-| external.influxdb.service.name                  | Name of the InfluxDB service                             | "influxdb"                               |
-| external.influxdb.service.apiPort               | TargetPort of the InfluxDB service for API               | 8083                                     |
-| external.influxdb.service.httpPort              | TargetPort of the InfluxDB service for HTTP              | 8086                                     |
-| external.influxdb.service.grpcPort              | TargetPort of the InfluxDB service for gRPC              | 8090                                     |
-| external.influxdb.persistence.storageClassName  | Storage class name for InfluxDB PVs                      | "hostpath"                               |
-| external.influxdb.persistence.storageSize       | Size of the InfluxDB PV                                  | "1Gi"                                    |
-| external.influxdb.persistence.storageSizeV1     | Size of the InfluxDB PV for v1 databases                 | "1Gi"                                    |
-| external.influxdb.persistence.claimName         | Name of the InfluxDBv2 PersistentVolumeClaim             | "influxdb2-pvc"                          |
-| external.influxdb.persistence.claimNameV1       | Name of the InfluxDBv1 PersistentVolumeClaim             | "influxdb-pvc"                           |
-| external.influxdb.persistence.pvName            | Name of the InfluxDBv2 PersistentVolume                  | "influxdb2-pv"                           |
-| external.influxdb.persistence.pvNameV1          | Name of the InfluxDBv1 PersistentVolume                  | "influxdb-pv"                            |
-
-
-####Nats common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.nats.appName                           | NATS application name                                    | "nats"                                   |
-| external.nats.port                              | Port for the NATS service                                | 4222                                     |
-| external.nats.version                           | NATS version                                             |                                          |
-| external.nats.service.type                      | Type of the NATS service                                 | "NodePort"                               |
-| external.nats.service.externalTrafficPolicy     | External traffic policy for the NATS service             | "Local"                                  |
-| external.nats.service.name                      | Name of the NATS service                                 | "nats"                                   |
-| external.nats.service.port                      | TargetPort of the NATS service                           | 4222                                     |
-
-
-####Kafka common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.kafka.appName                          | Kafka application name                                   | "kafka"                                  |
-| external.kafka.version                          | Kafka version                                            | 2.2.0                                    |
-| external.kafka.port                             | Port for the Kafka service                               | 9092                                     |
-| external.kafka.external.hostname                | Name which will be advertised to external clients. Clients which use (default) port 9094       | "localhost"
-| external.kafka.service.name                     | Name of the Kafka service                                | "kafka"                                  |
-| external.kafka.service.port                     | TargetPort of the Kafka service                          | 9092                                     |
-| external.kafka.service.portOutside              | Port for Kafka client outside of the cluster | 9094                              |
-| external.kafka.persistence.storageClassName     | Storage class name for Kafka PVs                         | "hostpath"                               |
-| external.kafka.persistence.storageSize          | Size of the Kafka PV                                     | "1Gi"                                    |
-| external.kafka.persistence.claimName            | Name of the Kafka PersistentVolumeClaim                  | "kafka-pvc"                              |
-| external.kafka.persistence.pvName               | Name of the Kafka PersistentVolume                       | "kafka-pv"                               |
+
+## Parameters
+
+### Common parameters
+
+| Parameter Name                | Description                                         | Value           |
+|-------------------------------|-----------------------------------------------------|-----------------|
+| deployment                    | Deployment type (lite or full)                      | lite            |
+| preferredBroker               | Preferred broker for deployment                     | "nats"          |
+| monitoringSystem              | Enable monitoring system (true/false)               | false           |
+| pullPolicy                    | Image pull policy                                   | "Always"        |
+| restartPolicy                 | Restart policy for the container                    | Always          |
+| persistentVolumeReclaimPolicy | Reclaim policy for persistent volumes               | "Delete"        |
+| persistentVolumeAccessModes   | Access mode for persistent volumes                  | "ReadWriteOnce" |
+| initialDelaySeconds           | Initial delay for liveness and readiness probes     | 60              |
+| periodSeconds                 | Interval between liveness and readiness probes      | 30              |
+| failureThreshold              | Number of consecutive failures for readiness probes | 30              |
+| hostPath                      | Host path for the application                       | ""              |
+
+### StreamPipes common parameters
+
+| Parameter Name                                | Description                                             | Value                                    |
+|-----------------------------------------------|---------------------------------------------------------|------------------------------------------|
+| streampipes.version                           | StreamPipes version                                     | "0.93.0-SNAPSHOT"                        |
+| streampipes.registry                          | StreamPipes registry URL                                | "apachestreampipes"                      |
+| streampipes.auth.secretName                   | The secret name for storing secrets                     | "sp-secrets"                             |
+| streampipes.auth.users.admin.user             | The initial admin user                                  | "admin@streampipes.apache.org"           |
+| streampipes.auth.users.admin.password         | The initial admin password (leave empty for autogen)    | "admin"                                  |
+| streampipes.auth.users.service.user           | The initial service account user                        | "sp-service-client"                      |
+| streampipes.auth.users.service.secret         | The initial service account secret                      | empty (auto-generated)                   |
+| streampipes.auth.encryption.passcode          | Passcode for value encryption                           | empty (auto-generated)                   |
+| streampipes.core.appName                      | StreamPipes backend application name                    | "backend"                                |
+| streampipes.core.port                         | StreamPipes backend port                                | 8030                                     |
+| streampipes.core.persistence.storageClassName | Storage class name for backend PVs                      | "hostpath"                               |
+| streampipes.core.persistence.storageSize      | Size of the backend PV                                  | "1Gi"                                    |
+| streampipes.core.persistence.claimName        | Name of the backend PersistentVolumeClaim               | "backend-pvc"                            |
+| streampipes.core.persistence.pvName           | Name of the backend PersistentVolume                    | "backend-pv"                             |
+| streampipes.core.service.name                 | Name of the backend service                             | "backend"                                |
+| streampipes.core.service.port                 | TargetPort of the StreamPipes backend service           | 8030                                     |
+| streampipes.ui.appName                        | StreamPipes UI application name                         | "ui"                                     |
+| streampipes.ui.resolverActive                 | Flag for enabling DNS resolver for Nginx proxy          | true                                     |
+| streampipes.ui.port                           | StreamPipes UI port                                     | 8088                                     |
+| streampipes.ui.resolver                       | DNS resolver for Nginx proxy                            | "kube-dns.kube-system.svc.cluster.local" |
+| streampipes.ui.service.name                   | Name of the UI service                                  | "ui"                                     |
+| streampipes.ui.service.type                   | Type of the UI service                                  | "ClusterIP"                              |
+| streampipes.ui.service.nodePort               | Node port for the UI service                            | 8088                                     |
+| streampipes.ui.service.port                   | TargetPort of the StreamPipes UI service                | 8088                                     |
+| streampipes.ingress.active                    | Flag for enabling Ingress for StreamPipes               | false                                    |
+| streampipes.ingress.annotations               | Annotations for Ingress                                 | {}                                       |
+| streampipes.ingress.host                      | Hostname for Ingress                                    | ""                                       |
+| streampipes.ingressroute.active               | Flag for enabling IngressRoute for StreamPipes          | true                                     |
+| streampipes.ingressroute.annotations          | Annotations for IngressRoute                            | {}                                       |
+| streampipes.ingressroute.entryPoints          | Entry points for IngressRoute                           | ["web", "websecure"]                     |
+| streampipes.ingressroute.host                 | Hostname for IngressRoute                               | ""                                       |
+| streampipes.ingressroute.certResolverActive   | Flag for enabling certificate resolver for IngressRoute | true                                     |
+| streampipes.ingressroute.certResolver         | Certificate resolver for IngressRoute                   | ""                                       |
+
+### Extensions common parameters
+
+| Parameter Name               | Description                               | Value               |
+|------------------------------|-------------------------------------------|---------------------|
+| extensions.iiot.appName      | IIoT extensions application name          | extensions-all-iiot |
+| extensions.iiot.port         | Port for the IIoT extensions application  | 8090                |
+| extensions.iiot.service.name | Name of the IIoT extensions service       | extensions-all-iiot |
+| extensions.iiot.service.port | TargetPort of the IIoT extensions service | 8090                |
+
+### External common parameters
+
+#### Consul common parameters
+
+| Parameter Name                               | Description                                        | Value        |
+|----------------------------------------------|----------------------------------------------------|--------------|
+| external.consul.appName                      | Consul application name                            | "consul"     |
+| external.consul.version                      | Consul version                                     | 1.14.3       |
+| external.consul.webPort                      | Port number for the Consul web interface           | 8500         |
+| external.consul.dnsPort                      | Port number for the Consul DNS interface           | 8600         |
+| external.consul.persistence.storageClassName | Storage class name for Consul PVs                  | "hostpath"   |
+| external.consul.persistence.storageSize      | Size of the Consul PV                              | "1Gi"        |
+| external.consul.persistence.claimName        | Name of the Consul PersistentVolumeClaim           | "consul-pvc" |
+| external.consul.persistence.pvName           | Name of the Consul PersistentVolume                | "consul-pv"  |
+| external.consul.service.name                 | Name of the Consul service                         | "consul"     |
+| external.consul.service.webPort              | TargetPort of the Consul service for web interface | 8500         |
+| external.consul.service.dnsPort              | TargetPort of the Consul service for DNS interface | 8600         |
+
+#### Couchdb common parameters
+
+| Parameter Name                                | Description                               | Value                  |
+|-----------------------------------------------|-------------------------------------------|------------------------|
+| external.couchdb.appName                      | CouchDB application name                  | "couchdb"              |
+| external.couchdb.version                      | CouchDB version                           | 3.3.1                  |
+| external.couchdb.user                         | CouchDB admin username                    | "admin"                |
+| external.couchdb.password                     | CouchDB admin password                    | empty (auto-generated) |
+| external.couchdb.port                         | Port for the CouchDB service              | 5984                   |
+| external.couchdb.service.name                 | Name of the CouchDB service               | "couchdb"              |
+| external.couchdb.service.port                 | TargetPort of the CouchDB service         | 5984                   |
+| external.couchdb.persistence.storageClassName | Storage class name for CouchDB PVs        | "hostpath"             |
+| external.couchdb.persistence.storageSize      | Size of the CouchDB PV                    | "1Gi"                  |
+| external.couchdb.persistence.claimName        | Name of the CouchDB PersistentVolumeClaim | "couchdb-pvc"          |
+| external.couchdb.persistence.pvName           | Name of the CouchDB PersistentVolume      | "couchdb-pv"           |
+
+#### Influxdb common parameters
+
+| Parameter Name                                 | Description                                  | Value                  |
+|------------------------------------------------|----------------------------------------------|------------------------|
+| external.influxdb.appName                      | InfluxDB application name                    | "influxdb"             |
+| external.influxdb.version                      | InfluxDB version                             | 2.6                    |
+| external.influxdb.username                     | InfluxDB admin username                      | "admin"                |
+| external.influxdb.password                     | InfluxDB admin password                      | empty (auto-generated) |
+| external.influxdb.adminToken                   | InfluxDB admin token                         | empty (auto-generated) |
+| external.influxdb.initOrg                      | InfluxDB initial organization                | "sp"                   |
+| external.influxdb.initBucket                   | InfluxDB initial bucket                      | "sp"                   |
+| external.influxdb.initMode                     | InfluxDB initialization mode                 | "setup"                |
+| external.influxdb.apiPort                      | Port number for the InfluxDB service (API)   | 8083                   |
+| external.influxdb.httpPort                     | Port number for the InfluxDB service (HTTP)  | 8086                   |
+| external.influxdb.grpcPort                     | Port number for the InfluxDB service (gRPC)  | 8090                   |
+| external.influxdb.service.name                 | Name of the InfluxDB service                 | "influxdb"             |
+| external.influxdb.service.apiPort              | TargetPort of the InfluxDB service for API   | 8083                   |
+| external.influxdb.service.httpPort             | TargetPort of the InfluxDB service for HTTP  | 8086                   |
+| external.influxdb.service.grpcPort             | TargetPort of the InfluxDB service for gRPC  | 8090                   |
+| external.influxdb.persistence.storageClassName | Storage class name for InfluxDB PVs          | "hostpath"             |
+| external.influxdb.persistence.storageSize      | Size of the InfluxDB PV                      | "1Gi"                  |
+| external.influxdb.persistence.storageSizeV1    | Size of the InfluxDB PV for v1 databases     | "1Gi"                  |
+| external.influxdb.persistence.claimName        | Name of the InfluxDBv2 PersistentVolumeClaim | "influxdb2-pvc"        |
+| external.influxdb.persistence.claimNameV1      | Name of the InfluxDBv1 PersistentVolumeClaim | "influxdb-pvc"         |
+| external.influxdb.persistence.pvName           | Name of the InfluxDBv2 PersistentVolume      | "influxdb2-pv"         |
+| external.influxdb.persistence.pvNameV1         | Name of the InfluxDBv1 PersistentVolume      | "influxdb-pv"          |
+
+#### Nats common parameters
+
+| Parameter Name                              | Description                                  | Value      |
+|---------------------------------------------|----------------------------------------------|------------|
+| external.nats.appName                       | NATS application name                        | "nats"     |
+| external.nats.port                          | Port for the NATS service                    | 4222       |
+| external.nats.version                       | NATS version                                 |            |
+| external.nats.service.type                  | Type of the NATS service                     | "NodePort" |
+| external.nats.service.externalTrafficPolicy | External traffic policy for the NATS service | "Local"    |
+| external.nats.service.name                  | Name of the NATS service                     | "nats"     |
+| external.nats.service.port                  | TargetPort of the NATS service               | 4222       |
+
+#### Kafka common parameters
+
+| Parameter Name                              | Description                                                                              | Value       |
+|---------------------------------------------|------------------------------------------------------------------------------------------|-------------|
+| external.kafka.appName                      | Kafka application name                                                                   | "kafka"     |
+| external.kafka.version                      | Kafka version                                                                            | 2.2.0       |
+| external.kafka.port                         | Port for the Kafka service                                                               | 9092        |
+| external.kafka.external.hostname            | Name which will be advertised to external clients. Clients which use (default) port 9094 | "localhost" |
+| external.kafka.service.name                 | Name of the Kafka service                                                                | "kafka"     |
+| external.kafka.service.port                 | TargetPort of the Kafka service                                                          | 9092        |
+| external.kafka.service.portOutside          | Port for Kafka client outside of the cluster                                             | 9094        |
+| external.kafka.persistence.storageClassName | Storage class name for Kafka PVs                                                         | "hostpath"  |
+| external.kafka.persistence.storageSize      | Size of the Kafka PV                                                                     | "1Gi"       |
+| external.kafka.persistence.claimName        | Name of the Kafka PersistentVolumeClaim                                                  | "kafka-pvc" |
+| external.kafka.persistence.pvName           | Name of the Kafka PersistentVolume                                                       | "kafka-pv"  |
 |
 
-####Zookeeper common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.zookeeper.appName                      | ZooKeeper application name                               | "zookeeper"                              |
-| external.zookeeper.version                      | ZooKeeper version                                        | 3.4.13                                   |
-| external.zookeeper.port                         | Port for the ZooKeeper service                           | 2181                                     |
-| external.zookeeper.service.name                 | Name of the ZooKeeper service                            | "zookeeper"                              |
-| external.zookeeper.service.port                 | TargetPort of the ZooKeeper service                      | 2181                                     |
-| external.zookeeper.persistence.storageClassName | Storage class name for ZooKeeper PVs                     | "hostpath"                               |
-| external.zookeeper.persistence.storageSize      | Size of the ZooKeeper PV                                 | "1Gi"                                    |
-| external.zookeeper.persistence.claimName        | Name of the ZooKeeper PersistentVolumeClaim              | "zookeeper-pvc"                          |
-| external.zookeeper.persistence.pvName           | Name of the ZooKeeper PersistentVolume                   | "zookeeper-pv"                           |
-
-
-####Pulsar common parameters
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| external.pulsar.appName                         | pulsar application name                                  | "pulsar"                                 |
-| external.pulsar.version                         | pulsar version                                           | 3.0.0                                    |
-| external.pulsar.port                            | Port for the pulsar service                              | 6650                                     |
-| external.pulsar.service.name                    | Name of the pulsar service                               | "pulsar"                                 |
-| external.pulsar.service.port                    | TargetPort of the pulsar service                         | 6650                                     |
-| external.pulsar.persistence.storageClassName    | Storage class name for pulsar PVs                        | "hostpath"                               |
-| external.pulsar.persistence.storageSize         | Size of the pulsar PV                                    | "1Gi"                                    |
-| external.pulsar.persistence.claimName           | Name of the pulsar PersistentVolumeClaim                 | "pulsar-pvc"                             |
-| external.pulsar.persistence.pvName              | Name of the pulsar PersistentVolume                      | "pulsar-pv"                              |
-
-###Monitoring common parameters
+#### Zookeeper common parameters
+
+| Parameter Name                                  | Description                                 | Value           |
+|-------------------------------------------------|---------------------------------------------|-----------------|
+| external.zookeeper.appName                      | ZooKeeper application name                  | "zookeeper"     |
+| external.zookeeper.version                      | ZooKeeper version                           | 3.4.13          |
+| external.zookeeper.port                         | Port for the ZooKeeper service              | 2181            |
+| external.zookeeper.service.name                 | Name of the ZooKeeper service               | "zookeeper"     |
+| external.zookeeper.service.port                 | TargetPort of the ZooKeeper service         | 2181            |
+| external.zookeeper.persistence.storageClassName | Storage class name for ZooKeeper PVs        | "hostpath"      |
+| external.zookeeper.persistence.storageSize      | Size of the ZooKeeper PV                    | "1Gi"           |
+| external.zookeeper.persistence.claimName        | Name of the ZooKeeper PersistentVolumeClaim | "zookeeper-pvc" |
+| external.zookeeper.persistence.pvName           | Name of the ZooKeeper PersistentVolume      | "zookeeper-pv"  |
+
+#### Pulsar common parameters
+
+| Parameter Name                               | Description                              | Value        |
+|----------------------------------------------|------------------------------------------|--------------|
+| external.pulsar.appName                      | pulsar application name                  | "pulsar"     |
+| external.pulsar.version                      | pulsar version                           | 3.0.0        |
+| external.pulsar.port                         | Port for the pulsar service              | 6650         |
+| external.pulsar.service.name                 | Name of the pulsar service               | "pulsar"     |
+| external.pulsar.service.port                 | TargetPort of the pulsar service         | 6650         |
+| external.pulsar.persistence.storageClassName | Storage class name for pulsar PVs        | "hostpath"   |
+| external.pulsar.persistence.storageSize      | Size of the pulsar PV                    | "1Gi"        |
+| external.pulsar.persistence.claimName        | Name of the pulsar PersistentVolumeClaim | "pulsar-pvc" |
+| external.pulsar.persistence.pvName           | Name of the pulsar PersistentVolume      | "pulsar-pv"  |
+
+### Monitoring common parameters
 
 #### Monitoring - Prometheus
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| prometheus.appName                              | Prometheus application name                              | "prometheus"                             |
-| prometheus.version                              | Prometheus version                                       | 2.45.0                                   |
-| prometheus.port                                 | Prometheus port                                          | 9090                                     |
-| prometheus.service.name                         | Prometheus service name                                  | "prometheus"                             |
-| prometheus.service.port                         | Prometheus service port                                  | 9090                                     |
-| prometheus.persistence.storageClassName         | Prometheus storage class name                            | "hostpath"                               |
-| prometheus.persistence.storageSize              | Prometheus storage size                                  | "2Gi"                                    |
-| prometheus.persistence.claimName                | Prometheus PVC claim name                                | "prometheus-pvc"                         |
-| prometheus.persistence.pvName                   | Prometheus PV name                                       | "prometheus-pv"                          |
-| prometheus.persistence.tokenStorageSize         | Prometheus token storage size                            | "16Ki"                                   |
-| prometheus.config.scrapeInterval                | Prometheus scrape interval                               | 10s                                      |
-| prometheus.config.evaluationInterval            | Prometheus evaluation interval                           | 15s                                      |
-| prometheus.config.backendJobName                | Prometheus backend job name                              | "backend"                                |
-| prometheus.config.extensionsName                | Prometheus extensions job name                           | "extensions-all-iiot"                    |
-| prometheus.config.tokenFileName                 | Prometheus token file name                               | "token"                                  |
-| prometheus.config.tokenFileDir                  | Prometheus token file directory                          | "/opt/data"
+
+| Parameter Name                          | Description                     | Value                 |
+|-----------------------------------------|---------------------------------|-----------------------|
+| prometheus.appName                      | Prometheus application name     | "prometheus"          |
+| prometheus.version                      | Prometheus version              | 2.45.0                |
+| prometheus.port                         | Prometheus port                 | 9090                  |
+| prometheus.service.name                 | Prometheus service name         | "prometheus"          |
+| prometheus.service.port                 | Prometheus service port         | 9090                  |
+| prometheus.persistence.storageClassName | Prometheus storage class name   | "hostpath"            |
+| prometheus.persistence.storageSize      | Prometheus storage size         | "2Gi"                 |
+| prometheus.persistence.claimName        | Prometheus PVC claim name       | "prometheus-pvc"      |
+| prometheus.persistence.pvName           | Prometheus PV name              | "prometheus-pv"       |
+| prometheus.persistence.tokenStorageSize | Prometheus token storage size   | "16Ki"                |
+| prometheus.config.scrapeInterval        | Prometheus scrape interval      | 10s                   |
+| prometheus.config.evaluationInterval    | Prometheus evaluation interval  | 15s                   |
+| prometheus.config.backendJobName        | Prometheus backend job name     | "backend"             |
+| prometheus.config.extensionsName        | Prometheus extensions job name  | "extensions-all-iiot" |
+| prometheus.config.tokenFileName         | Prometheus token file name      | "token"               |
+| prometheus.config.tokenFileDir          | Prometheus token file directory | "/opt/data"           |
 
 #### Monitoring - Grafana
-| Parameter Name                                  | Description                                              | Value                                    |
-|-------------------------------------------------|----------------------------------------------------------|------------------------------------------|
-| grafana.appName                                 | Grafana application name                                 | "grafana"                                |
-| grafana.version                                 | Grafana version                                          | 10.1.2                                   |
-| grafana.port                                    | Grafana port                                             | 3000                                     |
-| grafana.service.name                            | Grafana service name                                     | "grafana"                                |
-| grafana.service.port                            | Grafana service port                                     | 3000                                     |
-| grafana.persistence.storageClassName            | Grafana storage class name                               | "hostpath"                               |
-| grafana.persistence.storageSize                 | Grafana storage size                                     | "1Gi"                                    |
-| grafana.persistence.claimName                   | Grafana PVC claim name                                   | "grafana-pvc"                            |
-| grafana.persistence.pvName                      | Grafana PV name                                          | "grafana-pv"                             |
+
+| Parameter Name                       | Description                | Value         |
+|--------------------------------------|----------------------------|---------------|
+| grafana.appName                      | Grafana application name   | "grafana"     |
+| grafana.version                      | Grafana version            | 10.1.2        |
+| grafana.port                         | Grafana port               | 3000          |
+| grafana.service.name                 | Grafana service name       | "grafana"     |
+| grafana.service.port                 | Grafana service port       | 3000          |
+| grafana.persistence.storageClassName | Grafana storage class name | "hostpath"    |
+| grafana.persistence.storageSize      | Grafana storage size       | "1Gi"         |
+| grafana.persistence.claimName        | Grafana PVC claim name     | "grafana-pvc" |
+| grafana.persistence.pvName           | Grafana PV name            | "grafana-pv"  |
 
 ## Bugs and Feature Requests
 
-If you've found a bug or have a feature that you'd love to see in StreamPipes, feel free to create an issue on [GitHub](https://github.com/apache/streampipes/issues).
+If you've found a bug or have a feature that you'd love to see in StreamPipes, feel free to create an issue
+on [GitHub](https://github.com/apache/streampipes/issues).
 
 ## Get help
-If you have any problems during the installation or questions around StreamPipes, you'll get help through one of our community channels:
+
+If you have any problems during the installation or questions around StreamPipes, you'll get help through one of our
+community channels:
 
 - [Slack](https://slack.streampipes.org)
 - [Mailing Lists](https://streampipes.apache.org/mailinglists.html)
@@ -308,20 +334,27 @@ If you have any problems during the installation or questions around StreamPipes
 And don't forget to follow us on [Twitter](https://twitter.com/streampipes)!
 
 ## Contribute
+
 We welcome contributions to StreamPipes. If you are interested in contributing to StreamPipes, let us know! You'll
- get to know an open-minded and motivated team working together to build the next IIoT analytics toolbox.
+get to know an open-minded and motivated team working together to build the next IIoT analytics toolbox.
 
 Here are some first steps in case you want to contribute:
+
 * Subscribe to our dev mailing list [dev-subscribe@streampipes.apache.org](dev-subscribe@streampipes.apache.org)
-* Send an email, tell us about your interests and which parts of StreamPipes you'd like to contribute (e.g., core or UI)!
+* Send an email, tell us about your interests and which parts of StreamPipes you'd like to contribute (e.g., core or
+  UI)!
 * Ask for a mentor who helps you to understand the code base and guides you through the first setup steps
-* Find an issue  on [GitHub](https://github.com/apache/streampipes/issues) which is tagged with a _good first issue_ tag
-* Have a look at our developer wiki at [https://cwiki.apache.org/confluence/display/STREAMPIPES](https://cwiki.apache.org/confluence/display/STREAMPIPES) to learn more about StreamPipes development.
+* Find an issue on [GitHub](https://github.com/apache/streampipes/issues) which is tagged with a _good first issue_ tag
+* Have a look at our developer wiki
+  at [https://cwiki.apache.org/confluence/display/STREAMPIPES](https://cwiki.apache.org/confluence/display/STREAMPIPES)
+  to learn more about StreamPipes development.
 
 Have fun!
 
 ## Feedback
+
 We'd love to hear your feedback! Subscribe to [users@streampipes.apache.org](mailto:users@streampipes.apache.org)
 
 ## License
+
 [Apache License 2.0](../LICENSE)
diff --git a/pom.xml b/pom.xml
index fef4b34cc0..81f79948d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
+
 <!--
 ~ Licensed to the Apache Software Foundation (ASF) under one or more
 ~ contributor license agreements.  See the NOTICE file distributed with
@@ -99,13 +100,13 @@
         <nats.version>2.17.0</nats.version>
         <netty.version>4.1.72.Final</netty.version>
         <okhttp.version>3.13.1</okhttp.version>
-        <opencsv.version>5.8</opencsv.version>
+        <opencsv.version>5.9</opencsv.version>
         <opennlp.version>2.1.0</opennlp.version>
         <plc4x.version>0.10.0</plc4x.version>
         <plexus-component-annotations.version>2.1.1</plexus-component-annotations.version>
         <plexus-interactivity-api.version>1.1</plexus-interactivity-api.version>
         <plexus-utils.version>3.5.0</plexus-utils.version>
-        <postgresql.version>42.6.0</postgresql.version>
+        <postgresql.version>42.7.0</postgresql.version>
         <protobuf.version>3.25.0</protobuf.version>
         <pulsar.version>2.10.1</pulsar.version>
         <quartz.version>2.3.2</quartz.version>
@@ -143,6 +144,7 @@
         <maven.dependency.check.plugin.version>8.4.0</maven.dependency.check.plugin.version>
         <maven-shade-plugin.version>3.4.1</maven-shade-plugin.version>
         <maven.version>3.8.7</maven.version>
+        <typescript-generator-maven-plugin.version>3.2.1263</typescript-generator-maven-plugin.version>
 
 
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -937,7 +939,7 @@
                                     <additionalJOption>-Xdoclint:none</additionalJOption>
                                     <doclint>none</doclint>
                                     <additionalJOption>-Xdoclint:none</additionalJOption>
-                                    <source>11</source>
+                                    <source>17</source>
                                 </configuration>
                             </execution>
                         </executions>
@@ -956,6 +958,11 @@
 
         <pluginManagement>
             <plugins>
+                <plugin>
+                    <groupId>cz.habarta.typescript-generator</groupId>
+                    <artifactId>typescript-generator-maven-plugin</artifactId>
+                    <version>${typescript-generator-maven-plugin.version}</version>
+                </plugin>
                 <plugin>
                     <groupId>net.revelc.code.formatter</groupId>
                     <artifactId>formatter-maven-plugin</artifactId>
diff --git a/prometheus-grafana/README.md b/prometheus-grafana/README.md
index b0e2a0aba7..a94c751394 100644
--- a/prometheus-grafana/README.md
+++ b/prometheus-grafana/README.md
@@ -17,7 +17,7 @@
   -->
 
 
-##Prometheus Configuration
+## Prometheus Configuration
 In the [dashboards](./prometheus) directory, you can find sample configuration file for Prometheus.
 
 ## Grafana Dashboards
@@ -25,5 +25,5 @@ In the [dashboards](./prometheus) directory, you can find sample configuration f
 In the [dashboards](./grafana/dashboards) directory, you can find sample grafana dashboards for several StreamPipes components.
 
 
-##note
+## note
 The metrics displayed in the [dashboards](. /grafana/dashboards) directory dashboards show metrics that have a filter condition related to the job_name, if you change the job_name in the example you must also change the condition in the grafana dashboard.
\ No newline at end of file
diff --git a/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/InfluxStore.java b/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/InfluxStore.java
index 4adf3f153c..4abb33d679 100644
--- a/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/InfluxStore.java
+++ b/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/InfluxStore.java
@@ -20,10 +20,10 @@ package org.apache.streampipes.dataexplorer.commons.influx;
 
 import org.apache.streampipes.commons.environment.Environment;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.dataexplorer.commons.influx.serializer.RawFieldSerializer;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.model.runtime.Event;
 import org.apache.streampipes.model.runtime.field.PrimitiveField;
-import org.apache.streampipes.model.schema.EventProperty;
 import org.apache.streampipes.model.schema.EventPropertyPrimitive;
 import org.apache.streampipes.model.schema.PropertyScope;
 import org.apache.streampipes.vocabulary.SO;
@@ -31,7 +31,6 @@ import org.apache.streampipes.vocabulary.XSD;
 
 import org.influxdb.InfluxDB;
 import org.influxdb.dto.Point;
-import org.influxdb.dto.Pong;
 import org.influxdb.dto.Query;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
@@ -49,6 +48,8 @@ public class InfluxStore {
   Map<String, String> sanitizedRuntimeNames = new HashMap<>();
   private InfluxDB influxDb = null;
 
+  private RawFieldSerializer rawFieldSerializer = new RawFieldSerializer();
+
   public InfluxStore(DataLakeMeasure measure,
                      InfluxConnectionSettings settings) {
     this.measure = measure;
@@ -76,7 +77,7 @@ public class InfluxStore {
     influxDb = InfluxClientProvider.getInfluxDBClient(settings);
 
     // Checking, if server is available
-    Pong response = influxDb.ping();
+    var response = influxDb.ping();
     if (response.getVersion().equalsIgnoreCase("unknown")) {
       throw new SpRuntimeException("Could not connect to InfluxDb Server: " + settings.getConnectionUrl());
     }
@@ -90,8 +91,8 @@ public class InfluxStore {
 
     // setting up the database
     influxDb.setDatabase(databaseName);
-    int batchSize = 2000;
-    int flushDuration = 500;
+    var batchSize = 2000;
+    var flushDuration = 500;
     influxDb.enableBatch(batchSize, flushDuration, TimeUnit.MILLISECONDS);
   }
 
@@ -122,28 +123,26 @@ public class InfluxStore {
     }
 
     // sanitize event
-    for (String key : event.getRaw().keySet()) {
+    for (var key : event.getRaw().keySet()) {
       if (InfluxDbReservedKeywords.KEYWORD_LIST.stream().anyMatch(k -> k.equalsIgnoreCase(key))) {
         event.renameFieldByRuntimeName(key, key + "_");
       }
     }
 
-    Long timestampValue = event.getFieldBySelector(measure.getTimestampField()).getAsPrimitive().getAsLong();
-    Point.Builder point =
+    var timestampValue = event.getFieldBySelector(measure.getTimestampField()).getAsPrimitive().getAsLong();
+    var point =
         Point.measurement(measure.getMeasureName()).time((long) timestampValue, TimeUnit.MILLISECONDS);
 
-    for (EventProperty ep : measure.getEventSchema().getEventProperties()) {
-      if (ep instanceof EventPropertyPrimitive) {
-        String runtimeName = ep.getRuntimeName();
-
-        // timestamp should not be added as a field
-        if (!measure.getTimestampField().endsWith(runtimeName)) {
-          String sanitizedRuntimeName = sanitizedRuntimeNames.get(runtimeName);
-
-          try {
-            var field = event.getOptionalFieldByRuntimeName(runtimeName);
+    for (var ep : measure.getEventSchema().getEventProperties()) {
+      var runtimeName = ep.getRuntimeName();
+      // timestamp should not be added as a field
+      if (!measure.getTimestampField().endsWith(runtimeName)) {
+        var sanitizedRuntimeName = sanitizedRuntimeNames.get(runtimeName);
+        var field = event.getOptionalFieldByRuntimeName(runtimeName);
+        try {
+          if (ep instanceof EventPropertyPrimitive) {
             if (field.isPresent()) {
-              PrimitiveField eventPropertyPrimitiveField = field.get().getAsPrimitive();
+              var eventPropertyPrimitiveField = field.get().getAsPrimitive();
               if (eventPropertyPrimitiveField.getRawValue() == null) {
                 nullFields.add(sanitizedRuntimeName);
               } else {
@@ -162,10 +161,18 @@ public class InfluxStore {
             } else {
               missingFields.add(runtimeName);
             }
-          } catch (SpRuntimeException iae) {
-            LOG.warn("Runtime exception while extracting field value of field {} - this field will be ignored",
-                runtimeName, iae);
+          } else {
+            // Since InfluxDB can't store non-primitive types, store them as string
+            // and deserialize later in downstream processes
+            if (field.isPresent()) {
+              handleNonPrimitiveMeasurementProperty(point, event, sanitizedRuntimeName);
+            } else {
+              missingFields.add(runtimeName);
+            }
           }
+        } catch (SpRuntimeException iae) {
+          LOG.warn("Runtime exception while extracting field value of field {} - this field will be ignored",
+              runtimeName, iae);
         }
       }
     }
@@ -189,7 +196,7 @@ public class InfluxStore {
                                          PrimitiveField eventPropertyPrimitiveField) {
     try {
       // Store property according to property type
-      String runtimeType = ep.getRuntimeType();
+      var runtimeType = ep.getRuntimeType();
       if (XSD.INTEGER.toString().equals(runtimeType)) {
         try {
           p.addField(preparedRuntimeName, eventPropertyPrimitiveField.getAsInt());
@@ -218,6 +225,15 @@ public class InfluxStore {
     }
   }
 
+  private void handleNonPrimitiveMeasurementProperty(Point.Builder p, Event event, String preparedRuntimeName) {
+    try {
+      var json = rawFieldSerializer.serialize(event.getRaw().get(preparedRuntimeName));
+      p.addField(preparedRuntimeName, json);
+    } catch (SpRuntimeException e) {
+      LOG.warn("Failed to serialize field {}, ignoring.", preparedRuntimeName);
+    }
+  }
+
   /**
    * Shuts down the connection to the InfluxDB server
    */
diff --git a/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/RawFieldSerializer.java b/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/RawFieldSerializer.java
new file mode 100644
index 0000000000..05380df10c
--- /dev/null
+++ b/streampipes-data-explorer-commons/src/main/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/RawFieldSerializer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dataexplorer.commons.influx.serializer;
+
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
+
+public class RawFieldSerializer {
+  protected ObjectMapper objectMapper;
+
+  public RawFieldSerializer() {
+    this.objectMapper = new ObjectMapper().activateDefaultTyping(
+        BasicPolymorphicTypeValidator.builder()
+            .allowIfBaseType(Object.class)
+            .build(),
+        ObjectMapper.DefaultTyping.EVERYTHING);
+  }
+
+  public String serialize(Object object) {
+    try {
+      return objectMapper.writeValueAsString(object);
+    } catch (JsonProcessingException e) {
+      throw new SpRuntimeException(e.getCause());
+    }
+  }
+
+  public Object deserialize(String json) {
+    try {
+      return objectMapper.readValue(json, Object.class);
+    } catch (JsonProcessingException e) {
+      throw new SpRuntimeException(e.getCause());
+    }
+  }
+}
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/iolink/IfmAlMqttAdapter.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/iolink/IfmAlMqttAdapter.java
index 5a0648112d..9c9fc05caf 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/iolink/IfmAlMqttAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/iolink/IfmAlMqttAdapter.java
@@ -77,7 +77,7 @@ public class IfmAlMqttAdapter implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, IfmAlMqttAdapter::new)
+        .create(ID, 0, IfmAlMqttAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.Generic, AdapterType.Manufacturing)
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/ros/RosBridgeAdapter.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/ros/RosBridgeAdapter.java
index c86ea31abe..675461ecbe 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/ros/RosBridgeAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/ros/RosBridgeAdapter.java
@@ -92,7 +92,7 @@ public class RosBridgeAdapter implements StreamPipesAdapter, ResolvesContainerPr
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, RosBridgeAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, RosBridgeAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.Manufacturing)
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
index bef53062ca..2af0b55e1f 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
@@ -43,7 +43,7 @@ public class MachineDataSimulatorAdapter implements StreamPipesAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, MachineDataSimulatorAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, MachineDataSimulatorAdapter::new)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
         .withCategory(AdapterType.Debugging)
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/FileReplayAdapter.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/FileReplayAdapter.java
index f9ad62a2c9..744982a888 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/FileReplayAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/FileReplayAdapter.java
@@ -80,7 +80,7 @@ public class FileReplayAdapter implements StreamPipesAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, FileReplayAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, FileReplayAdapter::new)
         .withSupportedParsers(
             new JsonParsers(),
             new CsvParser(),
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpServerProtocol.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpServerProtocol.java
index 5fc242cc1f..c068e31593 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpServerProtocol.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpServerProtocol.java
@@ -103,7 +103,7 @@ public class HttpServerProtocol implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, HttpServerProtocol::new)
+        .create(ID, 0, HttpServerProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpStreamProtocol.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpStreamProtocol.java
index ade3d66cb5..376f20f294 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpStreamProtocol.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/protocol/stream/HttpStreamProtocol.java
@@ -106,7 +106,7 @@ public class HttpStreamProtocol implements StreamPipesAdapter, IPullAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, HttpStreamProtocol::new)
+        .create(ID, 0, HttpStreamProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/image/stream/ImageStreamAdapter.java b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/image/stream/ImageStreamAdapter.java
index 52758308d1..405afdc1c1 100644
--- a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/image/stream/ImageStreamAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/image/stream/ImageStreamAdapter.java
@@ -46,7 +46,7 @@ public class ImageStreamAdapter implements StreamPipesAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, ImageStreamAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, ImageStreamAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .requiredIntegerParameter(Labels.withId(ImageZipUtils.INTERVAL_KEY))
diff --git a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/iss/IssAdapter.java b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/iss/IssAdapter.java
index b2d53617a2..bf9cb5bcb7 100644
--- a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/iss/IssAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/iss/IssAdapter.java
@@ -103,7 +103,7 @@ public class IssAdapter implements StreamPipesAdapter, IPullAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, IssAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, IssAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.OpenData)
diff --git a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioMQTTAdapter.java b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioMQTTAdapter.java
index a9edfc5cf5..4211057832 100644
--- a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioMQTTAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioMQTTAdapter.java
@@ -53,7 +53,7 @@ public class NetioMQTTAdapter implements StreamPipesAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, NetioMQTTAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, NetioMQTTAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.Energy)
diff --git a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioRestAdapter.java b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioRestAdapter.java
index 998115bc5d..20989d98fa 100644
--- a/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioRestAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters/src/main/java/org/apache/streampipes/connect/adapters/netio/NetioRestAdapter.java
@@ -125,7 +125,7 @@ public class NetioRestAdapter implements StreamPipesAdapter, IPullAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, NetioRestAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, NetioRestAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.Energy)
diff --git a/streampipes-extensions/streampipes-connectors-influx/src/main/java/org/apache/streampipes/extensions/connectors/influx/adapter/InfluxDbStreamAdapter.java b/streampipes-extensions/streampipes-connectors-influx/src/main/java/org/apache/streampipes/extensions/connectors/influx/adapter/InfluxDbStreamAdapter.java
index 8c9ff23755..495edc2824 100644
--- a/streampipes-extensions/streampipes-connectors-influx/src/main/java/org/apache/streampipes/extensions/connectors/influx/adapter/InfluxDbStreamAdapter.java
+++ b/streampipes-extensions/streampipes-connectors-influx/src/main/java/org/apache/streampipes/extensions/connectors/influx/adapter/InfluxDbStreamAdapter.java
@@ -57,7 +57,7 @@ public class InfluxDbStreamAdapter implements StreamPipesAdapter {
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    var builder = AdapterConfigurationBuilder.create(ID, InfluxDbStreamAdapter::new)
+    var builder = AdapterConfigurationBuilder.create(ID, 0, InfluxDbStreamAdapter::new)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN);
 
diff --git a/streampipes-extensions/streampipes-connectors-kafka/src/main/java/org/apache/streampipes/extensions/connectors/kafka/adapter/KafkaProtocol.java b/streampipes-extensions/streampipes-connectors-kafka/src/main/java/org/apache/streampipes/extensions/connectors/kafka/adapter/KafkaProtocol.java
index f8b0340d51..e7df3dcc15 100644
--- a/streampipes-extensions/streampipes-connectors-kafka/src/main/java/org/apache/streampipes/extensions/connectors/kafka/adapter/KafkaProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-kafka/src/main/java/org/apache/streampipes/extensions/connectors/kafka/adapter/KafkaProtocol.java
@@ -145,7 +145,7 @@ public class KafkaProtocol implements StreamPipesAdapter, SupportsRuntimeConfig
     latestAlternative.setSelected(true);
 
     return AdapterConfigurationBuilder
-        .create(ID, KafkaProtocol::new)
+        .create(ID, 0, KafkaProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-connectors-mqtt/src/main/java/org/apache/streampipes/extensions/connectors/mqtt/adapter/MqttProtocol.java b/streampipes-extensions/streampipes-connectors-mqtt/src/main/java/org/apache/streampipes/extensions/connectors/mqtt/adapter/MqttProtocol.java
index b27f2205f0..1399794a89 100644
--- a/streampipes-extensions/streampipes-connectors-mqtt/src/main/java/org/apache/streampipes/extensions/connectors/mqtt/adapter/MqttProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-mqtt/src/main/java/org/apache/streampipes/extensions/connectors/mqtt/adapter/MqttProtocol.java
@@ -60,7 +60,7 @@ public class MqttProtocol implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, MqttProtocol::new)
+        .create(ID, 0, MqttProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
diff --git a/streampipes-extensions/streampipes-connectors-nats/src/main/java/org/apache/streampipes/extensions/connectors/nats/adapter/NatsProtocol.java b/streampipes-extensions/streampipes-connectors-nats/src/main/java/org/apache/streampipes/extensions/connectors/nats/adapter/NatsProtocol.java
index 5be2374586..6cbb2a07ae 100644
--- a/streampipes-extensions/streampipes-connectors-nats/src/main/java/org/apache/streampipes/extensions/connectors/nats/adapter/NatsProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-nats/src/main/java/org/apache/streampipes/extensions/connectors/nats/adapter/NatsProtocol.java
@@ -108,7 +108,7 @@ public class NatsProtocol implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, NatsProtocol::new)
+        .create(ID, 0, NatsProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withCategory(AdapterType.Generic)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
index 5df4f6329d..6af0bd28fd 100644
--- a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
+++ b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
@@ -143,7 +143,7 @@ public class OpcUaAdapter implements StreamPipesAdapter, IPullAdapter, SupportsR
     boolean emptyValueReceived = false;
     List<DataValue> returnValues =
         response.get(this.getPollingInterval().value(), this.getPollingInterval().timeUnit());
-    if (returnValues.size() == 0) {
+    if (returnValues.isEmpty()) {
       emptyValueReceived = true;
       LOG.warn("Empty value object returned - event will not be sent");
     } else {
diff --git a/streampipes-extensions/streampipes-connectors-plc/src/main/java/org/apache/streampipes/extensions/connectors/plc/adapter/modbus/Plc4xModbusAdapter.java b/streampipes-extensions/streampipes-connectors-plc/src/main/java/org/apache/streampipes/extensions/connectors/plc/adapter/modbus/Plc4xModbusAdapter.java
index 6938ead546..1487c77ae7 100644
--- a/streampipes-extensions/streampipes-connectors-plc/src/main/java/org/apache/streampipes/extensions/connectors/plc/adapter/modbus/Plc4xModbusAdapter.java
+++ b/streampipes-extensions/streampipes-connectors-plc/src/main/java/org/apache/streampipes/extensions/connectors/plc/adapter/modbus/Plc4xModbusAdapter.java
@@ -266,14 +266,14 @@ public class Plc4xModbusAdapter implements StreamPipesAdapter, IPullAdapter {
   }
 
   /**
-   * Describe the adapter adapter and define what user inputs are required. Currently users can just select one node,
+   * Describe the adapter and define what user inputs are required. Currently users can just select one node,
    * this will be extended in the future
    *
    * @return description of adapter
    */
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, Plc4xModbusAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 0, Plc4xModbusAdapter::new)
         .withLocales(Locales.EN)
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withCategory(AdapterType.Manufacturing)
diff --git a/streampipes-extensions/streampipes-connectors-pulsar/src/main/java/org/apache/streampipes/extensions/connectors/pulsar/adapter/PulsarProtocol.java b/streampipes-extensions/streampipes-connectors-pulsar/src/main/java/org/apache/streampipes/extensions/connectors/pulsar/adapter/PulsarProtocol.java
index 04ea26dead..c22567564d 100644
--- a/streampipes-extensions/streampipes-connectors-pulsar/src/main/java/org/apache/streampipes/extensions/connectors/pulsar/adapter/PulsarProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-pulsar/src/main/java/org/apache/streampipes/extensions/connectors/pulsar/adapter/PulsarProtocol.java
@@ -94,7 +94,7 @@ public class PulsarProtocol implements StreamPipesAdapter, SupportsRuntimeConfig
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, PulsarProtocol::new)
+        .create(ID, 0, PulsarProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-connectors-rocketmq/src/main/java/org/apache/streampipes/extensions/connectors/rocketmq/adapter/RocketMQProtocol.java b/streampipes-extensions/streampipes-connectors-rocketmq/src/main/java/org/apache/streampipes/extensions/connectors/rocketmq/adapter/RocketMQProtocol.java
index 02970e3630..adedfbab38 100644
--- a/streampipes-extensions/streampipes-connectors-rocketmq/src/main/java/org/apache/streampipes/extensions/connectors/rocketmq/adapter/RocketMQProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-rocketmq/src/main/java/org/apache/streampipes/extensions/connectors/rocketmq/adapter/RocketMQProtocol.java
@@ -73,7 +73,7 @@ public class RocketMQProtocol implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, RocketMQProtocol::new)
+        .create(ID, 0, RocketMQProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-connectors-tubemq/src/main/java/org/apache/streampipes/extensions/connectors/tubemq/adapter/TubeMQProtocol.java b/streampipes-extensions/streampipes-connectors-tubemq/src/main/java/org/apache/streampipes/extensions/connectors/tubemq/adapter/TubeMQProtocol.java
index 84ed9b8289..8fe027fe9a 100644
--- a/streampipes-extensions/streampipes-connectors-tubemq/src/main/java/org/apache/streampipes/extensions/connectors/tubemq/adapter/TubeMQProtocol.java
+++ b/streampipes-extensions/streampipes-connectors-tubemq/src/main/java/org/apache/streampipes/extensions/connectors/tubemq/adapter/TubeMQProtocol.java
@@ -102,7 +102,7 @@ public class TubeMQProtocol implements StreamPipesAdapter {
   @Override
   public IAdapterConfiguration declareConfig() {
     return AdapterConfigurationBuilder
-        .create(ID, TubeMQProtocol::new)
+        .create(ID, 0, TubeMQProtocol::new)
         .withSupportedParsers(Parsers.defaultParsers())
         .withAssets(Assets.DOCUMENTATION, Assets.ICON)
         .withLocales(Locales.EN)
diff --git a/streampipes-extensions/streampipes-processors-enricher-jvm/pom.xml b/streampipes-extensions/streampipes-processors-enricher-jvm/pom.xml
index 0412231f7b..bf759ac5ff 100644
--- a/streampipes-extensions/streampipes-processors-enricher-jvm/pom.xml
+++ b/streampipes-extensions/streampipes-processors-enricher-jvm/pom.xml
@@ -45,6 +45,12 @@
             <groupId>org.graalvm.js</groupId>
             <artifactId>js-scriptengine</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.streampipes</groupId>
+            <artifactId>streampipes-test-utils</artifactId>
+            <version>0.95.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/streampipes-extensions/streampipes-processors-enricher-jvm/src/main/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/SizeMeasureProcessor.java b/streampipes-extensions/streampipes-processors-enricher-jvm/src/main/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/SizeMeasureProcessor.java
index 850afe333a..78e5a82f33 100644
--- a/streampipes-extensions/streampipes-processors-enricher-jvm/src/main/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/SizeMeasureProcessor.java
+++ b/streampipes-extensions/streampipes-processors-enricher-jvm/src/main/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/SizeMeasureProcessor.java
@@ -43,7 +43,7 @@ import java.io.ObjectOutputStream;
 
 public class SizeMeasureProcessor extends StreamPipesDataProcessor {
 
-  private static final String SIZE_UNIT = "sizeUnit";
+  static final String SIZE_UNIT = "sizeUnit";
   static final String BYTE_SIZE = "BYTE";
   static final String KILOBYTE_SIZE = "KILOBYTE";
   static final String MEGABYTE_SIZE = "MEGABYTE";
diff --git a/streampipes-model-client/pom.xml b/streampipes-model-client/pom.xml
index 59452446eb..aed5bbfbfd 100644
--- a/streampipes-model-client/pom.xml
+++ b/streampipes-model-client/pom.xml
@@ -60,7 +60,6 @@
             <plugin>
                 <groupId>cz.habarta.typescript-generator</groupId>
                 <artifactId>typescript-generator-maven-plugin</artifactId>
-                <version>3.2.1263</version>
                 <executions>
                     <execution>
                         <id>generate</id>
diff --git a/streampipes-model/pom.xml b/streampipes-model/pom.xml
index 7a4dae6a6c..be1c634faf 100644
--- a/streampipes-model/pom.xml
+++ b/streampipes-model/pom.xml
@@ -78,7 +78,6 @@
             <plugin>
                 <groupId>cz.habarta.typescript-generator</groupId>
                 <artifactId>typescript-generator-maven-plugin</artifactId>
-                <version>3.2.1263</version>
                 <executions>
                     <execution>
                         <id>generate</id>
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLinkBuilder.java b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLinkBuilder.java
new file mode 100644
index 0000000000..68f127a0f6
--- /dev/null
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLinkBuilder.java
@@ -0,0 +1,119 @@
+/*
+ * 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.model.assets;
+
+/**
+ * Builder class for creating instances of {@link AssetLink}.
+ * <p>
+ * Example usage:
+ * <pre>
+ * {@code
+ * AssetLink assetLink = AssetLinkBuilder.create()
+ *     .withResourceId("123")
+ *     .withLinkType("type")
+ *     .withLinkLabel("label")
+ *     .withQueryHint("hint")
+ *     .withEditingDisabled(false)
+ *     .build();
+ * }
+ * </pre>
+ */
+public class AssetLinkBuilder {
+  private final AssetLink assetLink;
+
+  /**
+   * Constructs a new AssetLinkBuilder.
+   */
+  private AssetLinkBuilder() {
+    this.assetLink = new AssetLink();
+  }
+
+  /**
+   * Static method to create a new instance of AssetLinkBuilder.
+   *
+   * @return A new instance of AssetLinkBuilder.
+   */
+  public static AssetLinkBuilder create() {
+    return new AssetLinkBuilder();
+  }
+
+  /**
+   * Sets the resource ID for the AssetLink being built.
+   *
+   * @param resourceId The resource ID to set.
+   * @return The AssetLinkBuilder instance for method chaining.
+   */
+  public AssetLinkBuilder withResourceId(String resourceId) {
+    this.assetLink.setResourceId(resourceId);
+    return this;
+  }
+
+  /**
+   * Sets the link type for the AssetLink being built.
+   *
+   * @param linkType The link type to set.
+   * @return The AssetLinkBuilder instance for method chaining.
+   */
+  public AssetLinkBuilder withLinkType(String linkType) {
+    this.assetLink.setLinkType(linkType);
+    return this;
+  }
+
+  /**
+   * Sets the link label for the AssetLink being built.
+   *
+   * @param linkLabel The link label to set.
+   * @return The AssetLinkBuilder instance for method chaining.
+   */
+  public AssetLinkBuilder withLinkLabel(String linkLabel) {
+    this.assetLink.setLinkLabel(linkLabel);
+    return this;
+  }
+
+  /**
+   * Sets the query hint for the AssetLink being built.
+   *
+   * @param queryHint The query hint to set.
+   * @return The AssetLinkBuilder instance for method chaining.
+   */
+  public AssetLinkBuilder withQueryHint(String queryHint) {
+    this.assetLink.setQueryHint(queryHint);
+    return this;
+  }
+
+  /**
+   * Sets whether editing is disabled for the AssetLink being built.
+   *
+   * @param editingDisabled Whether editing is disabled.
+   * @return The AssetLinkBuilder instance for method chaining.
+   */
+  public AssetLinkBuilder withEditingDisabled(boolean editingDisabled) {
+    this.assetLink.setEditingDisabled(editingDisabled);
+    return this;
+  }
+
+  /**
+   * Builds and returns the final instance of AssetLink.
+   *
+   * @return The constructed AssetLink instance.
+   */
+  public AssetLink build() {
+    return this.assetLink;
+  }
+}
\ No newline at end of file
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/base/NamedStreamPipesEntity.java b/streampipes-model/src/main/java/org/apache/streampipes/model/base/NamedStreamPipesEntity.java
index 1e7e1a8bbe..30981732f4 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/base/NamedStreamPipesEntity.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/base/NamedStreamPipesEntity.java
@@ -128,16 +128,6 @@ public abstract class NamedStreamPipesEntity implements Serializable {
     this.iconUrl = iconUrl;
   }
 
-  @Deprecated
-  public String getUri() {
-    return elementId;
-  }
-
-  @Deprecated
-  public void setUri(String uri) {
-    this.elementId = uri;
-  }
-
   public String getDom() {
     return dom;
   }
diff --git a/streampipes-rest-extensions/src/main/java/org/apache/streampipes/rest/extensions/connect/AdapterWorkerResource.java b/streampipes-rest-extensions/src/main/java/org/apache/streampipes/rest/extensions/connect/AdapterWorkerResource.java
index 2877fc456e..56475c355d 100644
--- a/streampipes-rest-extensions/src/main/java/org/apache/streampipes/rest/extensions/connect/AdapterWorkerResource.java
+++ b/streampipes-rest-extensions/src/main/java/org/apache/streampipes/rest/extensions/connect/AdapterWorkerResource.java
@@ -76,11 +76,13 @@ public class AdapterWorkerResource extends AbstractSharedRestInterface {
 
     try {
       adapterManagement.invokeAdapter(adapterStreamDescription);
-      String responseMessage = "Stream adapter with id " + adapterStreamDescription.getUri() + " successfully started";
+      String responseMessage = "Stream adapter with id "
+              + adapterStreamDescription.getElementId()
+              + " successfully started";
       logger.info(responseMessage);
       return ok(Notifications.success(responseMessage));
     } catch (AdapterException e) {
-      logger.error("Error while starting adapter with id " + adapterStreamDescription.getUri(), e);
+      logger.error("Error while starting adapter with id " + adapterStreamDescription.getElementId(), e);
       return serverError(SpLogMessage.from(e));
     }
   }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
index b3f7b6719e..3bcbd6a17b 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
@@ -18,8 +18,9 @@
 
 package org.apache.streampipes.rest.impl;
 
+import org.apache.streampipes.model.client.user.Principal;
+import org.apache.streampipes.model.client.user.PrincipalType;
 import org.apache.streampipes.model.message.Notifications;
-import org.apache.streampipes.model.message.SuccessMessage;
 import org.apache.streampipes.rest.ResetManagement;
 import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
@@ -34,6 +35,8 @@ import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 
+import java.util.ArrayList;
+
 @Path("/v2/reset")
 public class ResetResource extends AbstractAuthGuardedRestResource {
   private static final Logger logger = LoggerFactory.getLogger(ResetResource.class);
@@ -44,7 +47,17 @@ public class ResetResource extends AbstractAuthGuardedRestResource {
   @Operation(summary = "Resets StreamPipes instance")
   public Response reset() {
     ResetManagement.reset(getAuthenticatedUsername());
-    SuccessMessage message = Notifications.success("Reset of system successfully performed");
+    var userStorage = getUserStorage();
+    // Delete all users other than current user (admin) and their resources
+    var allUsers = new ArrayList<Principal>(userStorage.getAllUsers());
+    for (var user : allUsers) {
+      if (user.getPrincipalType() == PrincipalType.USER_ACCOUNT
+          && !user.getPrincipalId().equals(getAuthenticatedUserSid())) {
+        ResetManagement.reset(user.getUsername());
+        userStorage.deleteUser(user.getPrincipalId());
+      }
+    }
+    var message = Notifications.success("Reset of system successfully performed");
     return ok(message);
   }
 
diff --git a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/stream/StreamBuilder.java b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/stream/StreamBuilder.java
index 9462a0daa0..f213f02a21 100644
--- a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/stream/StreamBuilder.java
+++ b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/stream/StreamBuilder.java
@@ -32,25 +32,25 @@ public class StreamBuilder {
   private SpDataStream stream;
   private EventSchema schema;
 
-  private StreamBuilder(String name, String description, String uri) {
+  private StreamBuilder(String name, String description, String elementId) {
     stream = new SpDataStream();
     stream.setName(name);
     stream.setDescription(description);
-    stream.setUri(uri);
+    stream.setElementId(elementId);
   }
 
-  private StreamBuilder(String uri) {
+  private StreamBuilder(String elementId) {
     stream = new SpDataStream();
-    stream.setUri(uri);
+    stream.setElementId(elementId);
     schema = new EventSchema();
   }
 
-  public static StreamBuilder createStream(String name, String description, String uri) {
-    return new StreamBuilder(name, description, uri);
+  public static StreamBuilder createStream(String name, String description, String elementId) {
+    return new StreamBuilder(name, description, elementId);
   }
 
-  public static StreamBuilder createStreamRestriction(String uri) {
-    return new StreamBuilder(uri);
+  public static StreamBuilder createStreamRestriction(String elementId) {
+    return new StreamBuilder(elementId);
   }
 
   public StreamBuilder property(String propertyName, URI propertyType, URI subclassOf) {
diff --git a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Filter.java b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Filter.java
index 64356a145f..57ff1595cb 100644
--- a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Filter.java
+++ b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Filter.java
@@ -29,7 +29,7 @@ public class Filter {
     return allElements
         .stream()
         .filter(e -> userElements.stream()
-            .anyMatch(u -> u.equals(e.getUri()))).collect(Collectors.toList());
+            .anyMatch(u -> u.equals(e.getElementId()))).collect(Collectors.toList());
   }
 
 }
diff --git a/ui/angular.json b/ui/angular.json
index 67a727a016..710ed50252 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -18,7 +18,7 @@
             "main": "src/main.ts",
             "tsConfig": "src/tsconfig.app.json",
             "polyfills": "src/polyfills.ts",
-            "allowedCommonJsDependencies": ["lodash", "codemirror"],
+            "allowedCommonJsDependencies": ["codemirror"],
             "assets": [
               "src/assets",
               {
@@ -34,7 +34,8 @@
               "node_modules/datatables.net/js/jquery.dataTables.js",
               "node_modules/jquery-ui-dist/jquery-ui.js",
               "node_modules/quill/dist/quill.js"
-            ]
+            ],
+            "outputHashing": "bundles"
           },
           "configurations": {
             "production": {
@@ -51,7 +52,7 @@
                 }
               ],
               "optimization": true,
-              "outputHashing": "all",
+              "outputHashing": "bundles",
               "sourceMap": false,
               "namedChunks": false,
               "aot": true,
diff --git a/ui/cypress/support/utils/DashboardUtils.ts b/ui/cypress/support/utils/DashboardUtils.ts
index 04a72a75dc..46268b4173 100644
--- a/ui/cypress/support/utils/DashboardUtils.ts
+++ b/ui/cypress/support/utils/DashboardUtils.ts
@@ -43,7 +43,11 @@ export class DashboardUtils {
         cy.dataCy('edit-dashboard-' + dashboardName).click();
     }
 
-    public static addWidget(pipelineName: string, widgetType: string) {
+    public static addWidget(
+        pipelineName: string,
+        widgetType: string,
+        options?,
+    ) {
         // Add raw data widget
         cy.dataCy('dashboard-add-widget').click();
 
@@ -52,11 +56,27 @@ export class DashboardUtils {
 
         // Select widget
         cy.dataCy('dashboard-select-widget-' + widgetType).click();
+
+        if (widgetType == 'area' || widgetType == 'line') {
+            cy.dataCy('min-y-axis-key').type(options.minYaxis);
+            cy.dataCy('max-y-axis-key').type(options.maxYaxis);
+        } else if (widgetType == 'gauge') {
+            cy.dataCy('min-key').type(options.minYaxis);
+            cy.dataCy('max-key').type(options.maxYaxis);
+        } else if (widgetType == 'status') {
+            cy.dataCy('interval-key').type(options.intervalKey);
+        } else if (widgetType == 'trafficlight') {
+            cy.dataCy('critical-value-key').type(options.criticalValue);
+            cy.dataCy('warning-range').type(options.warningRange);
+        }
+
         // optional configure widget
         cy.dataCy('dashboard-new-widget-next-btn').click();
 
         // Finish edit mode
         cy.dataCy('dashboard-save-edit-mode').click();
+
+        cy.wait(1000);
     }
 
     public static validateRawWidgetEvents(amountOfEvents: number) {
@@ -64,4 +84,37 @@ export class DashboardUtils {
             .its('length')
             .should('be.gte', amountOfEvents);
     }
+
+    public static removeWidgetFromDashboard(dashboardName: string) {
+        cy.visit('#/dashboard');
+        cy.dataCy('edit-dashboard-' + dashboardName).click();
+        cy.dataCy('widget-remove-button').click();
+    }
+
+    public static testWidget(
+        widgetType: string,
+        dashboardName: string,
+        options?: {
+            minYaxis?: string;
+            maxYaxis?: string;
+            intervalKey?: string;
+            criticalValue?: string;
+            warningRange?: string;
+        },
+    ) {
+        DashboardUtils.addWidget('Persist_simulator', widgetType, options);
+        if (widgetType == 'status') {
+            cy.get(`sp-dashboard-${widgetType.toLowerCase()}-widget`).should(
+                'be.visible',
+            );
+        } else if (widgetType == 'trafficlight') {
+            cy.get(`sp-traffic-light-widget`).should('be.visible');
+        } else {
+            cy.get(`sp-${widgetType.toLowerCase()}-widget`).should(
+                'be.visible',
+            );
+        }
+
+        DashboardUtils.removeWidgetFromDashboard(dashboardName);
+    }
 }
diff --git a/ui/cypress/support/utils/UserUtils.ts b/ui/cypress/support/utils/UserUtils.ts
index 60e735e597..fb505aa81a 100644
--- a/ui/cypress/support/utils/UserUtils.ts
+++ b/ui/cypress/support/utils/UserUtils.ts
@@ -27,6 +27,15 @@ export class UserUtils {
         .addRole(UserRole.ROLE_ADMIN)
         .build();
 
+    public static adapterAndPipelineAdminUser = UserBuilder.create(
+        'anpadmin@streampipes.apache.org',
+    )
+        .setName('anpadmin')
+        .setPassword('anpadmin')
+        .addRole(UserRole.ROLE_PIPELINE_ADMIN)
+        .addRole(UserRole.ROLE_CONNECT_ADMIN)
+        .build();
+
     public static goToUserConfiguration() {
         cy.visit('#/configuration/security');
     }
@@ -42,9 +51,11 @@ export class UserUtils {
         cy.dataCy('new-user-password-repeat').type(user.password);
 
         // Set role
-        cy.dataCy('role-' + user.role[0])
-            .children()
-            .click();
+        for (var i = 0; i < user.role.length; i++) {
+            cy.dataCy('role-' + user.role[i])
+                .children()
+                .click();
+        }
         cy.dataCy('new-user-enabled').children().click();
 
         // Store
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts b/ui/cypress/support/utils/connect/ConnectUtils.ts
index b6a3f28e80..22c57d853b 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -22,6 +22,8 @@ import { ConnectEventSchemaUtils } from '../ConnectEventSchemaUtils';
 import { DataLakeUtils } from '../datalake/DataLakeUtils';
 import { ConnectBtns } from './ConnectBtns';
 import { AdapterBuilder } from '../../builder/AdapterBuilder';
+import { UserUtils } from '../UserUtils';
+import { PipelineUtils } from '../PipelineUtils';
 
 export class ConnectUtils {
     public static testAdapter(adapterConfiguration: AdapterInput) {
@@ -193,11 +195,10 @@ export class ConnectUtils {
 
     public static deleteAdapter() {
         // Delete adapter
-        cy.visit('#/connect');
+        this.goToConnect();
 
         cy.dataCy('delete-adapter').should('have.length', 1);
-        cy.dataCy('delete-adapter').click();
-        cy.dataCy('delete-adapter-confirmation').click();
+        this.clickDelete();
         cy.dataCy('adapter-deletion-in-progress', { timeout: 10000 }).should(
             'be.visible',
         );
@@ -207,6 +208,72 @@ export class ConnectUtils {
         );
     }
 
+    public static deleteAdapterAndAssociatedPipelines(switchUserCheck = false) {
+        // Delete adapter and associated pipelines
+        this.goToConnect();
+        cy.dataCy('delete-adapter').should('have.length', 1);
+        this.clickDelete();
+        cy.dataCy('delete-adapter-and-associated-pipelines-confirmation', {
+            timeout: 10000,
+        }).should('be.visible');
+        cy.dataCy(
+            'delete-adapter-and-associated-pipelines-confirmation',
+        ).click();
+        cy.dataCy('adapter-deletion-in-progress', { timeout: 10000 }).should(
+            'be.visible',
+        );
+        if (switchUserCheck) {
+            cy.switchUser(UserUtils.adapterAndPipelineAdminUser);
+        }
+        this.checkAdapterAndAssociatedPipelinesDeleted();
+    }
+
+    // NOTE: this function will leave the adapter and associated pipelines running,
+    // please make sure to clean up after calling this function
+    public static deleteAdapterAndAssociatedPipelinesPermissionDenied() {
+        // Associated pipelines not owned by the user (unless admin) should not be deleted during adapter deletion
+        this.goToConnect();
+        cy.dataCy('delete-adapter').should('have.length', 1);
+        this.clickDelete();
+        cy.dataCy('delete-adapter-and-associated-pipelines-confirmation', {
+            timeout: 10000,
+        }).should('be.visible');
+        cy.dataCy(
+            'delete-adapter-and-associated-pipelines-confirmation',
+        ).click();
+        cy.dataCy('adapter-deletion-permission-denied', {
+            timeout: 10000,
+        }).should('be.visible');
+        cy.get('.sp-dialog-actions').click();
+        this.checkAdapterNotDeleted();
+    }
+
+    public static clickDelete() {
+        cy.dataCy('delete-adapter').click();
+        cy.dataCy('delete-adapter-confirmation').click();
+    }
+
+    public static checkAdapterNotDeleted() {
+        this.goToConnect();
+        cy.dataCy('delete-adapter', { timeout: 20000 }).should(
+            'have.length',
+            1,
+        );
+    }
+
+    public static checkAdapterAndAssociatedPipelinesDeleted() {
+        this.goToConnect();
+        cy.dataCy('delete-adapter', { timeout: 20000 }).should(
+            'have.length',
+            0,
+        );
+        PipelineUtils.goToPipelines();
+        cy.dataCy('delete-pipeline', { timeout: 10000 }).should(
+            'have.length',
+            0,
+        );
+    }
+
     public static setUpPreprocessingRuleTest(): AdapterInput {
         const adapterConfiguration = AdapterBuilder.create('File_Stream')
             .setStoreInDataLake()
diff --git a/ui/cypress/support/utils/datalake/DataLakeUtils.ts b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
index eb4968c394..9b567b31fb 100644
--- a/ui/cypress/support/utils/datalake/DataLakeUtils.ts
+++ b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
@@ -30,6 +30,11 @@ export class DataLakeUtils {
         cy.visit('#/dataexplorer');
     }
 
+    public static initDataLakeTests() {
+        cy.initStreamPipesTest();
+        DataLakeUtils.loadRandomDataSetIntoDataLake();
+    }
+
     public static getDataLakeTestSetAdapter(
         name: string,
         storeInDataLake: boolean = true,
@@ -88,7 +93,7 @@ export class DataLakeUtils {
         DataLakeUtils.createAndEditDataView(dataViewName);
 
         DataLakeUtils.selectTimeRange(
-            new Date(2015, 10, 20, 22, 44),
+            new Date(2020, 10, 20, 22, 44),
             DataLakeUtils.getFutureDate(),
         );
         // DataLakeUtils.addNewWidget();
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 7b299a8547..1eddf947c3 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -51,7 +51,6 @@
         "jshint": "^2.13.6",
         "konva": "9.2.0",
         "leaflet": "1.9.3",
-        "lodash": "4.17.21",
         "material-icons": "^1.13.1",
         "ngx-color-picker": "^14.0.0",
         "ngx-echarts": "^15.0.3",
@@ -86,7 +85,7 @@
         "@types/jqueryui": "^1.12.16",
         "@types/jshint": "^2.12.1",
         "@types/leaflet": "^1.9.0",
-        "@types/node": "^20.8.10",
+        "@types/node": "^20.9.4",
         "@types/rx": "^4.1.2",
         "@types/showdown": "1.9.4",
         "@typescript-eslint/eslint-plugin": "5.57.1",
@@ -116,7 +115,7 @@
         "ng-lint-staged": "^12.0.4",
         "ng-packagr": "^15.1.2",
         "path": "^0.12.7",
-        "prettier": "3.0.3",
+        "prettier": "3.1.0",
         "raw-loader": "^4.0.2",
         "resize-observer-polyfill": "^1.5.1",
         "to-string-loader": "^1.2.0",
@@ -7425,9 +7424,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "20.8.10",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
-      "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
+      "version": "20.9.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
+      "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
       "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
@@ -22015,9 +22014,9 @@
       }
     },
     "node_modules/prettier": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
-      "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
+      "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
       "dev": true,
       "bin": {
         "prettier": "bin/prettier.cjs"
@@ -33827,9 +33826,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "20.8.10",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
-      "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
+      "version": "20.9.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
+      "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
       "dev": true,
       "requires": {
         "undici-types": "~5.26.4"
@@ -45251,9 +45250,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
-      "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
+      "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
       "dev": true
     },
     "pretty-bytes": {
diff --git a/ui/package.json b/ui/package.json
index 3dfaab6b7d..89408d6362 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -73,7 +73,6 @@
     "jshint": "^2.13.6",
     "konva": "9.2.0",
     "leaflet": "1.9.3",
-    "lodash": "4.17.21",
     "material-icons": "^1.13.1",
     "ngx-color-picker": "^14.0.0",
     "ngx-echarts": "^15.0.3",
@@ -108,7 +107,7 @@
     "@types/jqueryui": "^1.12.16",
     "@types/jshint": "^2.12.1",
     "@types/leaflet": "^1.9.0",
-    "@types/node": "^20.8.10",
+    "@types/node": "^20.9.4",
     "@types/rx": "^4.1.2",
     "@types/showdown": "1.9.4",
     "@typescript-eslint/eslint-plugin": "5.57.1",
@@ -138,7 +137,7 @@
     "ng-lint-staged": "^12.0.4",
     "ng-packagr": "^15.1.2",
     "path": "^0.12.7",
-    "prettier": "3.0.3",
+    "prettier": "3.1.0",
     "raw-loader": "^4.0.2",
     "resize-observer-polyfill": "^1.5.1",
     "to-string-loader": "^1.2.0",
diff --git a/ui/src/app/assets/components/asset-overview/asset-overview.component.html b/ui/src/app/assets/components/asset-overview/asset-overview.component.html
index 8e73c95bd8..d08ce9d0e8 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.html
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.html
@@ -198,6 +198,18 @@
                                                     >delete</i
                                                 >
                                             </button>
+                                            <button
+                                                color="accent"
+                                                mat-icon-button
+                                                matTooltip="Download asset"
+                                                data-cy="download"
+                                                matTooltipPosition="above"
+                                                (click)="downloadAsset(asset)"
+                                            >
+                                                <i class="material-icons"
+                                                    >download</i
+                                                >
+                                            </button>
                                         </span>
                                     </div>
                                 </td>
diff --git a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
index cf942f1951..31c8ddf0ba 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
@@ -32,6 +32,9 @@ import { SpAssetRoutes } from '../../assets.routes';
 import { AssetUploadDialogComponent } from '../../dialog/asset-upload/asset-upload-dialog.component';
 import { Router } from '@angular/router';
 import { SpCreateAssetDialogComponent } from '../../dialog/create-asset/create-asset-dialog.component';
+import { DataExportService } from '../../../configuration/export/data-export.service';
+import { mergeMap } from 'rxjs/operators';
+import { saveAs } from 'file-saver';
 
 @Component({
     selector: 'sp-asset-overview-component',
@@ -50,6 +53,7 @@ export class SpAssetOverviewComponent implements OnInit {
         private breadcrumbService: SpBreadcrumbService,
         private dialogService: DialogService,
         private router: Router,
+        private dataExportService: DataExportService,
     ) {}
 
     ngOnInit(): void {
@@ -147,4 +151,18 @@ export class SpAssetOverviewComponent implements OnInit {
                 this.loadAssets();
             });
     }
+
+    downloadAsset(asset: SpAssetModel) {
+        this.dataExportService
+            .getExportPreview([asset._id])
+            .pipe(
+                mergeMap(preview =>
+                    this.dataExportService.triggerExport(preview),
+                ),
+            )
+            .subscribe((data: Blob) => {
+                const blob = new Blob([data], { type: 'application/zip' });
+                saveAs(blob, 'assetExport');
+            });
+    }
 }
diff --git a/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.html b/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.html
index c5a73031b4..0f08cec5c4 100644
--- a/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.html
+++ b/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.html
@@ -67,6 +67,7 @@
             fxLayoutAlign="center center"
             fxLayout="column"
             style="width: 100%"
+            data-cy="adapter-deletion-permission-denied"
         >
             <b>
                 <h4>
diff --git a/ui/src/app/dashboard/components/widget/dashboard-widget.component.html b/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
index a0ae353ddb..9ed91e5b88 100644
--- a/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
+++ b/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
@@ -31,7 +31,12 @@
                 >
                     <i class="material-icons">settings</i>
                 </button>
-                <button mat-icon-button color="white" (click)="removeWidget()">
+                <button
+                    mat-icon-button
+                    color="white"
+                    (click)="removeWidget()"
+                    data-cy="widget-remove-button"
+                >
                     <i class="material-icons">clear</i>
                 </button>
             </div>
diff --git a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html b/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html
index d722812b71..1ce4b2df1f 100644
--- a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html
+++ b/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html
@@ -87,6 +87,7 @@
             matTooltip="Reload"
             matTooltipPosition="above"
             (click)="reloadData()"
+            data-cy="reload-data-view-button"
         >
             <i class="material-icons mr-0">autorenew</i>
         </button>
diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
index 82490189ce..69a4463a7f 100644
--- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
+++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
@@ -70,7 +70,10 @@
                     *ngIf="!editMode"
                     [attr.data-cy]="
                         'more-options-' +
-                        configuredWidget.baseAppearanceConfig.widgetTitle
+                        configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
+                            ' ',
+                            ''
+                        )
                     "
                 >
                     <mat-icon>more_vert</mat-icon>
@@ -86,7 +89,10 @@
                         *ngIf="hasDataExplorerWritePrivileges"
                         [attr.data-cy]="
                             'start-edit-' +
-                            configuredWidget.baseAppearanceConfig.widgetTitle
+                            configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
+                                ' ',
+                                ''
+                            )
                         "
                     >
                         <mat-icon>edit</mat-icon>
diff --git a/ui/src/app/data-explorer/components/widgets/indicator/config/indicator-chart-widget-config.component.html b/ui/src/app/data-explorer/components/widgets/indicator/config/indicator-chart-widget-config.component.html
index c3c634e956..db188fea02 100644
--- a/ui/src/app/data-explorer/components/widgets/indicator/config/indicator-chart-widget-config.component.html
+++ b/ui/src/app/data-explorer/components/widgets/indicator/config/indicator-chart-widget-config.component.html
@@ -39,6 +39,7 @@
                 currentlyConfiguredWidget.visualizationConfig.deltaField
             "
             (changeSelectedProperty)="updateDelta($event)"
+            data-cy="data-explorer-select-delta-field"
         >
         </sp-select-property>
     </div>
diff --git a/ui/src/app/data-explorer/components/widgets/map/config/map-widget-config.component.html b/ui/src/app/data-explorer/components/widgets/map/config/map-widget-config.component.html
index c6f16fb719..4c8601544a 100644
--- a/ui/src/app/data-explorer/components/widgets/map/config/map-widget-config.component.html
+++ b/ui/src/app/data-explorer/components/widgets/map/config/map-widget-config.component.html
@@ -25,6 +25,7 @@
                 .selectedLatitudeProperty
         "
         (changeSelectedProperty)="setSelectedLatitudeProperty($event)"
+        data-cy="data-view-map-select-latitude"
     >
     </sp-select-property>
 
@@ -36,6 +37,7 @@
                 .selectedLongitudeProperty
         "
         (changeSelectedProperty)="setSelectedLongitudeProperty($event)"
+        data-cy="data-view-map-select-longitude"
     >
     </sp-select-property>
 
@@ -73,6 +75,7 @@
                     .selectedMarkerOrTrace
             "
             (selectionChange)="triggerViewRefresh()"
+            data-cy="data-view-map-select-marker-or-trace"
         >
             <mat-option [value]="'marker'">Marker</mat-option>
             <mat-option [value]="'trace'">Trace</mat-option>
diff --git a/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts b/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts
index 77fd2fd79d..6bce49cabc 100644
--- a/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts
+++ b/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts
@@ -78,8 +78,8 @@ export class TableWidgetComponent
                 a[event.active] > b[event.active]
                     ? 1
                     : b[event.active] > a[event.active]
-                    ? -1
-                    : 0,
+                      ? -1
+                      : 0,
             );
         }
 
@@ -88,8 +88,8 @@ export class TableWidgetComponent
                 a[event.active] > b[event.active]
                     ? -1
                     : b[event.active] > a[event.active]
-                    ? 1
-                    : 0,
+                      ? 1
+                      : 0,
             );
         }
 
@@ -98,8 +98,8 @@ export class TableWidgetComponent
                 a['timestamp'] > b['timestamp']
                     ? 1
                     : b['timestamp'] > a['timestamp']
-                    ? -1
-                    : 0,
+                      ? -1
+                      : 0,
             );
         }
     }
diff --git a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
index b8e0478d1a..a86eaed620 100644
--- a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
+++ b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
@@ -44,7 +44,6 @@ import {
 import { EditorService } from '../../services/editor.service';
 import { DialogService, PanelType } from '@streampipes/shared-ui';
 import { CompatibleElementsComponent } from '../../dialog/compatible-elements/compatible-elements.component';
-import { cloneDeep } from 'lodash';
 import { Subscription } from 'rxjs';
 import { JsplumbFactoryService } from '../../services/jsplumb-factory.service';
 
@@ -131,9 +130,7 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
             );
         this.pipelineElementCssType = this.pipelineElement.type;
 
-        this.isDataSource =
-            this.pipelineElement.type === 'stream' ||
-            this.pipelineElement.type === 'set';
+        this.isDataSource = this.pipelineElement.type === 'stream';
 
         if (
             this.isDataSource ||
@@ -162,7 +159,7 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
     }
 
     initRecs(pipelineElementDomId) {
-        const clonedModel: PipelineElementConfig[] = cloneDeep(
+        const clonedModel: PipelineElementConfig[] = this.deepCopy(
             this.rawPipelineModel,
         );
         const currentPipeline = this.objectProvider.makePipeline(clonedModel);
@@ -170,13 +167,13 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
             .recommendPipelineElement(currentPipeline, pipelineElementDomId)
             .subscribe(result => {
                 if (result.success) {
-                    this.possibleElements = cloneDeep(
+                    this.possibleElements = this.deepCopy(
                         this.pipelineElementRecommendationService.collectPossibleElements(
                             this.allElements,
                             result.possibleElements,
                         ),
                     );
-                    this.recommendedElements = cloneDeep(
+                    this.recommendedElements = this.deepCopy(
                         this.pipelineElementRecommendationService.populateRecommendedList(
                             this.allElements,
                             result.recommendedElements,
@@ -227,4 +224,28 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
     ngOnDestroy(): void {
         this.pipelineElementConfiguredObservable.unsubscribe();
     }
+
+    deepCopy(obj) {
+        let clone: any = {};
+        if (
+            obj === null ||
+            typeof obj !== 'object' ||
+            Array.isArray(obj) ||
+            obj === undefined
+        ) {
+            return obj;
+        }
+
+        if (Array.isArray(obj)) {
+            clone = obj.map(item => this.deepCopy(item));
+        }
+
+        for (const key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                clone[key] = this.deepCopy(obj[key]);
+            }
+        }
+
+        return clone;
+    }
 }
diff --git a/ui/src/app/files/components/file-overview/file-overview.component.html b/ui/src/app/files/components/file-overview/file-overview.component.html
index d221556f86..00ab831e65 100644
--- a/ui/src/app/files/components/file-overview/file-overview.component.html
+++ b/ui/src/app/files/components/file-overview/file-overview.component.html
@@ -33,9 +33,13 @@
             <ng-container matColumnDef="filetype">
                 <th mat-header-cell *matHeaderCellDef>Filetype</th>
                 <td mat-cell *matCellDef="let fileMetadata">
-                    <span class="filetype-container">{{
-                        fileMetadata.filetype
-                    }}</span>
+                    <span
+                        class="filetype-container"
+                        [style.background-color]="
+                            getFileColor(fileMetadata.filetype)
+                        "
+                        >{{ fileMetadata.filetype }}</span
+                    >
                 </td>
             </ng-container>
             <ng-container matColumnDef="uploaded">
diff --git a/ui/src/app/files/components/file-overview/file-overview.component.ts b/ui/src/app/files/components/file-overview/file-overview.component.ts
index 5479b51105..472de9117d 100644
--- a/ui/src/app/files/components/file-overview/file-overview.component.ts
+++ b/ui/src/app/files/components/file-overview/file-overview.component.ts
@@ -38,6 +38,8 @@ export class FileOverviewComponent implements OnInit {
     paginator: MatPaginator;
     pageSize = 1;
 
+    private fileTypeColors: { [key: string]: string } = {};
+
     constructor(
         private filesService: FilesService,
         private dialog: MatDialog,
@@ -89,6 +91,27 @@ export class FileOverviewComponent implements OnInit {
             });
     }
 
+    getFileColor(fileType: string) {
+        if (!this.fileTypeColors.hasOwnProperty(fileType)) {
+            this.fileTypeColors[fileType] = this.generateColorHash(fileType);
+        }
+
+        return this.fileTypeColors[fileType];
+    }
+
+    private generateColorHash(fileType: string) {
+        let hash = 0;
+
+        fileType.split('').forEach(char => {
+            hash = char.charCodeAt(0) + ((hash << 5) - hash);
+        });
+
+        const color = (Math.abs(hash) & 0x00ffffff).toString(16).toUpperCase();
+        const paddedColor = color.padStart(6, '0');
+
+        return `#${paddedColor}`;
+    }
+
     @ViewChild(MatPaginator) set content(paginator: MatPaginator) {
         this.paginator = paginator;
     }


Test patch:
diff --git a/streampipes-data-explorer-commons/src/test/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/TestRawFieldSerializer.java b/streampipes-data-explorer-commons/src/test/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/TestRawFieldSerializer.java
new file mode 100644
index 0000000000..c9b85695d1
--- /dev/null
+++ b/streampipes-data-explorer-commons/src/test/java/org/apache/streampipes/dataexplorer/commons/influx/serializer/TestRawFieldSerializer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dataexplorer.commons.influx.serializer;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestRawFieldSerializer {
+  private RawFieldSerializer rawFieldSerializer = new RawFieldSerializer();
+  private Map<String, Object> primitives = new HashMap<String, Object>();
+
+  public TestRawFieldSerializer() {
+    primitives.put("Integer", 1);
+    primitives.put("Long", 1L);
+    primitives.put("Float", 1.0f);
+    primitives.put("Double", 1.0d);
+    primitives.put("Boolean", true);
+    primitives.put("String", "1");
+  }
+
+  // Test able to deserialize back the original data
+  @Test
+  public void testRawFieldSerializerListInMap() {
+    var rawListField = new ArrayList<Object>();
+    rawListField.addAll(primitives.values());
+
+    var rawNestedField = new HashMap<String, Object>();
+    rawNestedField.putAll(primitives);
+    rawNestedField.put("List", rawListField);
+
+    var json = rawFieldSerializer.serialize(rawNestedField);
+
+    assertEquals(rawNestedField, rawFieldSerializer.deserialize(json));
+  }
+
+  @Test
+  public void testRawFieldSerializerMapInList() {
+    var rawNestedField = new HashMap<String, Object>();
+    rawNestedField.putAll(primitives);
+
+    var rawListField = new ArrayList<Object>();
+    rawListField.addAll(primitives.values());
+    rawListField.add(rawNestedField);
+
+    var json = rawFieldSerializer.serialize(rawListField);
+
+    assertEquals(rawListField, rawFieldSerializer.deserialize(json));
+  }
+}
diff --git a/streampipes-extensions/streampipes-processors-enricher-jvm/src/test/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/TestSizeMeasureProcessor.java b/streampipes-extensions/streampipes-processors-enricher-jvm/src/test/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/TestSizeMeasureProcessor.java
new file mode 100644
index 0000000000..4702c35af0
--- /dev/null
+++ b/streampipes-extensions/streampipes-processors-enricher-jvm/src/test/java/org/apache/streampipes/processors/enricher/jvm/processor/sizemeasure/TestSizeMeasureProcessor.java
@@ -0,0 +1,150 @@
+/*
+ * 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.processors.enricher.jvm.processor.sizemeasure;
+
+
+
+import org.apache.streampipes.extensions.api.pe.routing.SpOutputCollector;
+import org.apache.streampipes.model.graph.DataProcessorDescription;
+import org.apache.streampipes.model.graph.DataProcessorInvocation;
+import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.model.runtime.EventFactory;
+import org.apache.streampipes.model.runtime.SchemaInfo;
+import org.apache.streampipes.model.runtime.SourceInfo;
+import org.apache.streampipes.model.staticproperty.OneOfStaticProperty;
+import org.apache.streampipes.test.extensions.api.StoreEventCollector;
+import org.apache.streampipes.test.generator.InvocationGraphGenerator;
+import org.apache.streampipes.test.generator.grounding.EventGroundingGenerator;
+import org.apache.streampipes.wrapper.params.compat.ProcessorParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.streampipes.processors.enricher.jvm.processor.sizemeasure.SizeMeasureProcessor.BYTE_SIZE;
+import static org.apache.streampipes.processors.enricher.jvm.processor.sizemeasure.SizeMeasureProcessor.KILOBYTE_SIZE;
+import static org.apache.streampipes.processors.enricher.jvm.processor.sizemeasure.SizeMeasureProcessor.MEGABYTE_SIZE;
+import static org.apache.streampipes.processors.enricher.jvm.processor.sizemeasure.SizeMeasureProcessor.SIZE_UNIT;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class TestSizeMeasureProcessor {
+
+  private static final Logger LOG = LoggerFactory.getLogger(TestSizeMeasureProcessor.class);
+
+  @org.junit.runners.Parameterized.Parameters
+  public static Iterable<Object[]> data() {
+
+    /*
+    249 bytes is the base size of an object when first tested.
+    Allowing up to 250 bytes comparison error in case of inconsistent base sizes across platforms
+     */
+    return Arrays.asList(new Object[][]{
+            { BYTE_SIZE, 10240 - 249, 10240.0, 250.0},
+            { KILOBYTE_SIZE, 10240 - 249, 10.0, 0.025},
+            { MEGABYTE_SIZE, (1024 * 1024) - 249, 1.0, 0.0025}
+    });
+  }
+
+  @Parameterized.Parameter
+  public String sizeUnit;
+
+  @Parameterized.Parameter(1)
+  public int numOfBytes;
+
+  @Parameterized.Parameter(2)
+  public double expectedSize;
+
+  @Parameterized.Parameter(3)
+  public double allowableError;
+
+  @Test
+  public void testSizeMeasureProcessor() {
+    SizeMeasureProcessor processor = new SizeMeasureProcessor();
+    DataProcessorDescription originalGraph = processor.declareModel();
+    originalGraph.setSupportedGrounding(EventGroundingGenerator.makeDummyGrounding());
+
+    DataProcessorInvocation graph =
+            InvocationGraphGenerator.makeEmptyInvocation(originalGraph);
+
+
+    ProcessorParams params = new ProcessorParams(graph);
+    params.extractor().getStaticPropertyByName(SIZE_UNIT, OneOfStaticProperty.class).getOptions()
+            .stream().filter(ot -> ot.getInternalName().equals(sizeUnit)).findFirst()
+            .get().setSelected(true);
+
+
+    SpOutputCollector eventCollector = new StoreEventCollector();
+
+    processor.onInvocation(params, eventCollector, null);
+
+    LOG.info("Sending event with numOfBytes "
+        + numOfBytes + ", sizeUnit: " + sizeUnit);
+    double size = sendEvents(processor, eventCollector);
+
+    LOG.info("Expected size is {}", expectedSize);
+    LOG.info("Actual size is {}", size);
+    LOG.info("Allowable error is {}", allowableError);
+    assertEquals(expectedSize, size, allowableError);
+  }
+
+  private Double sendEvents(SizeMeasureProcessor processor, SpOutputCollector spOut) {
+    double size = 0.0;
+    Event event = makeEvent(numOfBytes);
+
+    processor.onEvent(event, spOut);
+    try {
+      Thread.sleep(100);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    try {
+      size = event.getFieldBySelector(SizeMeasureProcessor.EVENT_SIZE)
+              .getAsPrimitive()
+              .getAsDouble();
+    } catch (IllegalArgumentException e) {
+      e.printStackTrace();
+    }
+    return size;
+  }
+  private Event makeEvent(int numOfBytes) {
+    Map<String, Object> map = new HashMap<>();
+    map.put("Test", new ObjectWithSize(numOfBytes));
+    return EventFactory.fromMap(map,
+            new SourceInfo("test" + "-topic", "s0"),
+            new SchemaInfo(null, new ArrayList<>()));
+  }
+
+  public static class ObjectWithSize implements Serializable {
+    private final byte[] data;
+
+    public ObjectWithSize(int numOfBytes) {
+      this.data = new byte[numOfBytes];
+    }
+  }
+}
\ No newline at end of file
diff --git a/streampipes-model/src/test/java/org/apache/streampipes/model/assets/AssetLinkBuilderTest.java b/streampipes-model/src/test/java/org/apache/streampipes/model/assets/AssetLinkBuilderTest.java
new file mode 100644
index 0000000000..4f237ebdbc
--- /dev/null
+++ b/streampipes-model/src/test/java/org/apache/streampipes/model/assets/AssetLinkBuilderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.model.assets;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class AssetLinkBuilderTest {
+
+  @Test
+  public void withoutEditingDisabled() {
+    AssetLink assetLink = AssetLinkBuilder.create()
+            .withResourceId("123")
+            .withLinkType("type")
+            .withLinkLabel("label")
+            .withQueryHint("hint")
+            .withEditingDisabled(false)
+            .build();
+
+    assertEquals("123", assetLink.getResourceId());
+    assertEquals("type", assetLink.getLinkType());
+    assertEquals("label", assetLink.getLinkLabel());
+    assertEquals("hint", assetLink.getQueryHint());
+    assertFalse(assetLink.isEditingDisabled());
+  }
+
+  @Test
+  public void withEditingDisabled() {
+    AssetLink assetLink = AssetLinkBuilder.create()
+            .withResourceId("456")
+            .withLinkType("anotherType")
+            .withLinkLabel("anotherLabel")
+            .withQueryHint("anotherHint")
+            .withEditingDisabled(true)
+            .build();
+
+    assertEquals("456", assetLink.getResourceId());
+    assertEquals("anotherType", assetLink.getLinkType());
+    assertEquals("anotherLabel", assetLink.getLinkLabel());
+    assertEquals("anotherHint", assetLink.getQueryHint());
+    assertTrue(assetLink.isEditingDisabled());
+  }
+}
diff --git a/ui/cypress/tests/adapter/enhancedDeleteAdapter.smoke.spec.ts b/ui/cypress/tests/adapter/enhancedDeleteAdapter.smoke.spec.ts
new file mode 100644
index 0000000000..6bb2cc3c59
--- /dev/null
+++ b/ui/cypress/tests/adapter/enhancedDeleteAdapter.smoke.spec.ts
@@ -0,0 +1,95 @@
+/*
+ * 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 { AdapterBuilder } from '../../support/builder/AdapterBuilder';
+import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
+import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
+import { UserUtils } from '../../support/utils/UserUtils';
+import { PipelineUtils } from '../../support/utils/PipelineUtils';
+
+const adapterName = 'simulator';
+
+const adapterInput = AdapterBuilder.create('Machine_Data_Simulator')
+    .setName(adapterName)
+    .addInput('input', 'wait-time-ms', '1000')
+    .build();
+
+const pipelineInput = PipelineBuilder.create('Pipeline Test')
+    .addSource(adapterName)
+    .addProcessingElement(
+        PipelineElementBuilder.create('field_renamer')
+            .addInput('drop-down', 'convert-property', 'timestamp')
+            .addInput('input', 'field-name', 't')
+            .build(),
+    )
+    .addSink(
+        PipelineElementBuilder.create('data_lake')
+            .addInput('input', 'db_measurement', 'demo')
+            .build(),
+    )
+    .build();
+
+describe('Test Enhanced Adapter Deletion', () => {
+    // In the beginning, create the non-admin user
+    before(() => {
+        cy.initStreamPipesTest();
+        UserUtils.addUser(UserUtils.adapterAndPipelineAdminUser);
+        cy.logout();
+    });
+
+    beforeEach('Setup Test', () => {
+        cy.visit('#/login');
+        cy.dataCy('login-email').type(
+            UserUtils.adapterAndPipelineAdminUser.email,
+        );
+        cy.dataCy('login-password').type(
+            UserUtils.adapterAndPipelineAdminUser.password,
+        );
+        cy.dataCy('login-button').click();
+        cy.wait(1000);
+    });
+
+    it('Test Delete Adapter and Associated Pipelines', () => {
+        ConnectUtils.testAdapter(adapterInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        ConnectUtils.deleteAdapterAndAssociatedPipelines();
+    });
+
+    it('Test Admin Should Be Able to Delete Adapter and Not Owned Associated Pipelines', () => {
+        // Let the user create the adapter and the pipeline
+        ConnectUtils.testAdapter(adapterInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        // Then let the admin delete them
+        cy.switchUser(UserUtils.adminUser);
+        ConnectUtils.deleteAdapterAndAssociatedPipelines(true);
+    });
+
+    it('Test Delete Adapter and Associated Pipelines Permission Denied', () => {
+        // Let the admin create the adapter and the pipeline
+        cy.switchUser(UserUtils.adminUser);
+        ConnectUtils.testAdapter(adapterInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        PipelineUtils.addPipeline(pipelineInput);
+        // Then the user shouldn't be able to delete them
+        cy.switchUser(UserUtils.adapterAndPipelineAdminUser);
+        ConnectUtils.deleteAdapterAndAssociatedPipelinesPermissionDenied();
+    });
+});
diff --git a/ui/cypress/tests/assetManagement/createAsset.spec.ts b/ui/cypress/tests/assetManagement/createAsset.spec.ts
index 1ff47dfb32..fb5c529013 100644
--- a/ui/cypress/tests/assetManagement/createAsset.spec.ts
+++ b/ui/cypress/tests/assetManagement/createAsset.spec.ts
@@ -89,9 +89,36 @@ describe('Creates a new adapter, add to assets and export assets', () => {
         cy.dataCy('import-button').click();
 
         // Check if import was successful
+        cy.visit('#/connect');
+        cy.dataCy('adapters-table').children().should('have.length', 1);
         cy.visit('#/assets');
         cy.dataCy('assets-table').should('have.length', 1);
+
+        // Export Asset via Assets page
+        cy.dataCy('download').click();
+
+        // Delete Adapter and Asset
+        cy.visit('#/connect');
+        cy.dataCy('delete-adapter').click();
+        cy.dataCy('delete-adapter-confirmation').click();
+
+        cy.visit('#/assets');
+        cy.dataCy('delete').click();
+
+        // Import downloaded Asset
+        cy.visit('#/configuration/export');
+        cy.dataCy('import-application-data-button').click();
+        cy.get('input[type="file"]').selectFile(
+            'cypress/downloads/assetExport.zip',
+            { force: true },
+        );
+        cy.dataCy('next-import-button').click();
+        cy.dataCy('import-button').click();
+
+        // Check if import was successful
         cy.visit('#/connect');
         cy.dataCy('adapters-table').children().should('have.length', 1);
+        cy.visit('#/assets');
+        cy.dataCy('assets-table').should('have.length', 1);
     });
 });
diff --git a/ui/cypress/tests/dashboard/dashboardWidgets.spec.ts b/ui/cypress/tests/dashboard/dashboardWidgets.spec.ts
new file mode 100644
index 0000000000..2ef797590c
--- /dev/null
+++ b/ui/cypress/tests/dashboard/dashboardWidgets.spec.ts
@@ -0,0 +1,81 @@
+/*
+ * 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 { DashboardUtils } from '../../support/utils/DashboardUtils';
+
+describe('Test All Widgets', () => {
+    const dashboardName = 'testDashboard';
+
+    beforeEach('Setup Test', () => {
+        cy.initStreamPipesTest();
+        ConnectUtils.addMachineDataSimulator('simulator', true);
+        DashboardUtils.goToDashboard();
+    });
+
+    it('Perform Test', () => {
+        DashboardUtils.addAndEditDashboard(dashboardName);
+
+        // Test area chart
+        DashboardUtils.testWidget('area', dashboardName, {
+            minYaxis: '0',
+            maxYaxis: '50',
+        });
+
+        // Test bar race chart
+        DashboardUtils.testWidget('bar-race', dashboardName);
+
+        // Test gauge widget
+        DashboardUtils.testWidget('gauge', dashboardName, {
+            minYaxis: '0',
+            maxYaxis: '50',
+        });
+
+        // Test html widget
+        DashboardUtils.testWidget('html', dashboardName);
+
+        // Test line widget
+        DashboardUtils.testWidget('line', dashboardName, {
+            minYaxis: '0',
+            maxYaxis: '50',
+        });
+
+        // Test raw widget
+        DashboardUtils.testWidget('raw', dashboardName);
+
+        // Test single value widget
+        DashboardUtils.testWidget('number', dashboardName);
+
+        // Test stacked line chart widget
+        DashboardUtils.testWidget('stacked-line-chart', dashboardName);
+
+        // Test status widget
+        DashboardUtils.testWidget('status', dashboardName, {
+            intervalKey: '5',
+        });
+
+        // Test table widget
+        DashboardUtils.testWidget('table', dashboardName);
+
+        // Test traffic light widget
+        DashboardUtils.testWidget('trafficlight', dashboardName, {
+            warningRange: '5',
+            criticalValue: '5',
+        });
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/2d-correlation.spec.ts b/ui/cypress/tests/datalake/widgets/2d-correlation.spec.ts
new file mode 100644
index 0000000000..4e799ef9c5
--- /dev/null
+++ b/ui/cypress/tests/datalake/widgets/2d-correlation.spec.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+
+describe('Test 2d Correlation View in Data Explorer', () => {
+    beforeEach('Setup Test', () => {
+        DataLakeUtils.initDataLakeTests();
+    });
+
+    it('Perform Test', () => {
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', '2D Correlation');
+
+        // Check if scatter plot is displayed
+        cy.get('g').should('have.class', 'scatterlayer mlayer');
+        cy.get('g.points', { timeout: 10000 })
+            .children()
+            .should('have.length', 10);
+
+        // Change from line plot to scatter plot
+        DataLakeUtils.selectVisualizationConfig();
+        cy.get('div').contains('Scatter').click();
+        cy.get('div').contains('Density').click();
+
+        // Check if scatter plot is displayed
+        cy.get('g').should('have.class', 'contourlayer mlayer');
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/table.ts b/ui/cypress/tests/datalake/widgets/distribution.spec.ts
similarity index 54%
rename from ui/cypress/tests/datalake/widgets/table.ts
rename to ui/cypress/tests/datalake/widgets/distribution.spec.ts
index 12ab68dedf..ec49019da9 100644
--- a/ui/cypress/tests/datalake/widgets/table.ts
+++ b/ui/cypress/tests/datalake/widgets/distribution.spec.ts
@@ -18,41 +18,32 @@
 
 import { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
 
-describe('Test Table View in Data Explorer', () => {
+describe('Test Distribution View in Data Explorer', () => {
     beforeEach('Setup Test', () => {
-        cy.login();
-        // cy.initStreamPipesTest()
-        // DataLakeUtils.loadRandomDataSetIntoDataLake();
+        DataLakeUtils.initDataLakeTests();
     });
 
     it('Perform Test', () => {
-        DataLakeUtils.goToDatalake();
-
-        // DataLakeUtils.createAndEditDataView();
-        // Click edit button
-        cy.dataCy('edit-data-view').click();
-
-        DataLakeUtils.addNewWidget();
-
-        DataLakeUtils.selectDataSet('Persist');
-
-        DataLakeUtils.dataConfigSelectAllFields();
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Distribution');
 
+        // Change field for histogram
         DataLakeUtils.selectVisualizationConfig();
+        cy.get('div').contains('randomtext (prepared_data #1)').click();
+        cy.get('div').contains('randomnumber (prepared_data #1)').click();
 
-        DataLakeUtils.selectVisualizationType('Table');
-
-        DataLakeUtils.clickCreateButton();
-
-        DataLakeUtils.selectTimeRange(
-            new Date(2020, 10, 20, 22, 44),
-            DataLakeUtils.getFutureDate(),
+        // Check if distribution chart is displayed
+        cy.get('sp-data-explorer-distribution-chart-widget').should(
+            'be.visible',
         );
 
-        cy.dataCy('data-explorer-table-row', { timeout: 10000 }).should(
-            'have.length',
-            10,
+        // Change from histogram to heatmap
+        DataLakeUtils.selectVisualizationConfig();
+        cy.get('div').contains('Histogram').click();
+        cy.get('div').contains('Value Heatmap').click();
+
+        // Check if distribution chart is displayed
+        cy.get('sp-data-explorer-distribution-chart-widget').should(
+            'be.visible',
         );
-        // Validate that X lines are available
     });
 });
diff --git a/ui/cypress/tests/datalake/widgets/heatmap.spec.ts b/ui/cypress/tests/datalake/widgets/heatmap.spec.ts
new file mode 100644
index 0000000000..9fa4ac34ac
--- /dev/null
+++ b/ui/cypress/tests/datalake/widgets/heatmap.spec.ts
@@ -0,0 +1,36 @@
+/*
+ * 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 { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+
+describe('Test Heatmap View in Data Explorer', () => {
+    beforeEach('Setup Test', () => {
+        DataLakeUtils.initDataLakeTests();
+    });
+
+    it('Perform Test', () => {
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Heatmap');
+
+        // Check checkbox
+        DataLakeUtils.selectVisualizationConfig();
+        cy.get('mat-checkbox input').click({ force: true });
+
+        // Check if heatmap chart is visible
+        cy.get('sp-data-explorer-heatmap-widget').should('be.visible');
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/indicator.spec.ts b/ui/cypress/tests/datalake/widgets/indicator.spec.ts
new file mode 100644
index 0000000000..a8499710da
--- /dev/null
+++ b/ui/cypress/tests/datalake/widgets/indicator.spec.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+
+describe('Test Indicator View in Data Explorer', () => {
+    beforeEach('Setup Test', () => {
+        DataLakeUtils.initDataLakeTests();
+    });
+
+    it('Perform Test', () => {
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Indicator');
+
+        // Check checkbox
+        DataLakeUtils.selectVisualizationConfig();
+        cy.get('mat-checkbox input').click({ force: true });
+        cy.dataCy('data-explorer-select-delta-field')
+            .click()
+            .get('mat-option')
+            .contains('count')
+            .click();
+
+        // Check if indicator is displayed
+        cy.get('g').should('have.class', 'indicatorlayer');
+        // Check if delta is displayed
+        cy.get('text').should('have.class', 'delta');
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/map.spec.ts b/ui/cypress/tests/datalake/widgets/map.spec.ts
new file mode 100644
index 0000000000..72e08da3d3
--- /dev/null
+++ b/ui/cypress/tests/datalake/widgets/map.spec.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+
+describe('Test Map View in Data Explorer', () => {
+    beforeEach('Setup Test', () => {
+        DataLakeUtils.initDataLakeTests();
+    });
+
+    it('Perform Test', () => {
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Map');
+
+        // Change marker positions
+        DataLakeUtils.selectVisualizationConfig();
+        cy.dataCy('data-view-map-select-latitude')
+            .click()
+            .get('mat-option')
+            .contains('randomnumber (prepared_data #1)')
+            .click();
+        cy.dataCy('data-view-map-select-longitude')
+            .click()
+            .get('mat-option')
+            .contains('randomnumber (prepared_data #1)')
+            .click();
+
+        // Check if map with markers is visible
+        cy.get('sp-data-explorer-map-widget').should('be.visible');
+        cy.get('img[alt=Marker]').should(
+            'have.attr',
+            'src',
+            'assets/img/marker-icon.png',
+        );
+
+        // Change from markers to trace
+        DataLakeUtils.selectVisualizationConfig();
+        cy.dataCy('data-view-map-select-marker-or-trace')
+            .click()
+            .get('mat-option')
+            .contains('Trace')
+            .click();
+
+        // Check if trace is visible
+        cy.get('path').should('have.class', 'leaflet-interactive');
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/table.spec.ts b/ui/cypress/tests/datalake/widgets/table.spec.ts
new file mode 100644
index 0000000000..4648835e77
--- /dev/null
+++ b/ui/cypress/tests/datalake/widgets/table.spec.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+
+describe('Test Table View in Data Explorer', () => {
+    beforeEach('Setup Test', () => {
+        DataLakeUtils.initDataLakeTests();
+    });
+
+    it('Perform Test', () => {
+        DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Table');
+
+        // Check if table is displayed correctly
+        cy.dataCy('data-explorer-table-row-timestamp', {
+            timeout: 10000,
+        }).should('have.length', 10);
+    });
+});
diff --git a/ui/cypress/tests/datalake/widgets/timeSeriesSave.ts b/ui/cypress/tests/datalake/widgets/timeSeriesSave.spec.ts
similarity index 78%
rename from ui/cypress/tests/datalake/widgets/timeSeriesSave.ts
rename to ui/cypress/tests/datalake/widgets/timeSeriesSave.spec.ts
index 846a3ec316..7be0a66603 100644
--- a/ui/cypress/tests/datalake/widgets/timeSeriesSave.ts
+++ b/ui/cypress/tests/datalake/widgets/timeSeriesSave.spec.ts
@@ -24,13 +24,11 @@ const dataSet = 'Persist';
 
 describe('Test if widget configuration is updated correctly', () => {
     beforeEach('Setup Test', () => {
-        cy.initStreamPipesTest();
-        DataLakeUtils.loadRandomDataSetIntoDataLake();
+        DataLakeUtils.initDataLakeTests();
 
         // Create first test data view with one time series widget
         DataLakeUtils.addDataViewAndTimeSeriesWidget(testView1, dataSet);
         DataLakeUtils.saveDataExplorerWidgetConfiguration();
-        // DataLakeUtils.clickStartTab();
 
         // Create second test data view with one time series widget
         DataLakeUtils.addDataViewAndTimeSeriesWidget(testView2, dataSet);
@@ -49,25 +47,28 @@ describe('Test if widget configuration is updated correctly', () => {
 });
 
 const runTestCase = (editOption: boolean) => {
-    // Go to test view 1 tab and then back to view 2
-    DataLakeUtils.clickTab(testView1);
-    DataLakeUtils.clickTab(testView2);
-
-    // Change one setting in widget
-    const widgetName = 'datalake_configuration';
+    // Visit settings of widget
+    const widgetName = 'prepared_data-time-series-chart';
 
     if (editOption) {
-        cy.dataCy('more-options-' + widgetName).click();
-        cy.dataCy('start-edit-' + widgetName).click();
+        DataLakeUtils.startEditWidget(widgetName);
     } else {
         cy.dataCy('options-data-explorer').click();
         cy.dataCy('options-edit-dashboard').click();
-        cy.dataCy('edit-' + widgetName).click();
     }
 
+    // Change first field from line plot to scatter plot
     DataLakeUtils.selectVisualizationConfig();
     cy.get('div').contains('Line').click();
     cy.get('div').contains('Scatter').click();
 
-    cy.get('path[class="scatterpts"]');
+    // Check if scatter plot is displayed
+    cy.get('g').should('have.class', 'scatterlayer mlayer');
+
+    // Change second field from line plot to bar plot
+    cy.get('div').contains('Line').click();
+    cy.get('div').contains('Bar').click();
+
+    // Check if bar plot is displayed
+    cy.get('g').should('have.class', 'barlayer mlayer');
 };