Repository: apache/streampipes
Pull request #2197 (2023-11-17 10:22:41)

#2151 & #2223: Implement E2E tests for enhanced adapter deletion and Extend reset REST endpoint to remove users
<!--
  ~ 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 #2151 

### 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

Added E2E tests for these 3 cases:

- An adapter and >1 pipelines are created by a non-admin user, the non-admin user should be able to delete the adapter and associated pipelines.
- A public adapter and >1 pipelines are created by a non-admin user, the admin should be able to delete the adapter and associated pipelines.
- A public adapter and >1 pipelines are created by admin (or any other users), the non-admin user should not be able to delete the adapter and the associated pipelines. After checking this, switch to admin's account to clean up (delete adapter, associated pipelines, non-admin user).

NOTE: I modified the logic of roles in `addUser()` of `UserUtil.ts` because I need the non-admin user to have both Connect and Pipeline admin privileges. I have run cypress tests locally and they all passed but I am not sure if this change will affect other tests in CI. For some reason the original logic is only taking the first role out of all roles.

Also sorry for closing my previous PR because I force pushed it to revert changes and separate my PR to 2 PRs 😅==



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..6e785d454d 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>
@@ -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-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..e9ef1b77d8 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -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/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..edf64ef933 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -86,7 +86,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 +116,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 +7425,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 +22015,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 +33827,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 +45251,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..3bcc385160 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -108,7 +108,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 +138,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/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/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..b409d77d52 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
@@ -131,9 +131,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 ||
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-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/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');
 };