This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch 3940-add-permission-support-to-assets
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/3940-add-permission-support-to-assets by this push:
     new 1dab692d3f feat(#3940): Add native data model for assets
1dab692d3f is described below

commit 1dab692d3fb1ad1fb1d45c1fc9710a38abf24c4e
Author: Dominik Riemer <[email protected]>
AuthorDate: Tue Nov 18 08:19:24 2025 +0100

    feat(#3940): Add native data model for assets
---
 .../apache/streampipes/model/assets/AssetLink.java |   9 ++
 .../{SpAssetModel.java => AssetLocation.java}      |  36 +----
 .../assets/{SpAssetModel.java => AssetSite.java}   |  38 +----
 .../apache/streampipes/model/assets/AssetType.java |   9 ++
 .../assets/{SpAssetModel.java => Isa95Type.java}   |  42 ++----
 .../assets/{SpAssetModel.java => LatLng.java}      |  35 +----
 .../apache/streampipes/model/assets/SpAsset.java   |  33 +++++
 .../streampipes/model/assets/SpAssetModel.java     |  49 +++++--
 .../manager/setup/CouchDbInstallationStep.java     |   6 +
 .../setup/tasks/AddAssetManagementViewTask.java    |  48 ++++++
 .../setup/tasks/CreateDefaultAssetTask.java        |   2 +-
 .../rest/impl/AssetManagementResource.java         |  76 ++++------
 .../core/migrations/AvailableMigrations.java       |   6 +-
 .../v0980/FixImportedPermissionsMigration.java     |  24 +--
 .../v099/AddAssetManagementViewMigration.java      |  52 +++++++
 .../migrations/v099/MoveAssetContentMigration.java | 120 +++++++++++++++
 .../streampipes/storage/api/INoSqlStorage.java     |   3 +
 .../storage/couchdb/CouchDbStorageManager.java     |  11 ++
 .../storage/couchdb/impl/AssetStorageImpl.java     |  23 +--
 .../src/lib/apis/asset-management.service.ts       |  19 +--
 .../src/lib/model/assets/asset.model.ts            |  66 +--------
 .../src/lib/model/gen/streampipes-model.ts         | 163 ++++++++++++++++++++-
 .../src/lib/services/isa95-type.service.ts         |   3 +-
 .../asset-browser-hierarchy.component.ts           |   5 +-
 .../asset-browser/asset-browser.service.ts         |  16 +-
 .../asset-link-configuration.component.ts          |  17 +--
 .../lib/services/asset-configuration.service.ts    |  36 +++--
 .../asset-details/base-asset-details.directive.ts  |  17 +--
 .../asset-details-site.component.ts                |   1 +
 .../edit-asset/asset-details.component.ts          |  34 ++---
 .../asset-selection-panel.component.ts             |   3 +
 .../view-asset/view-asset.component.ts             |  14 +-
 .../asset-overview/asset-overview.component.html   |  12 +-
 .../asset-overview/asset-overview.component.ts     |  57 ++++---
 .../manage-site/manage-site-dialog.component.ts    |   2 +-
 .../adapter-asset-configuration.service.ts         |  25 ++--
 .../data-explorer/dialog/asset-dialog.component.ts |   4 +-
 37 files changed, 692 insertions(+), 424 deletions(-)

diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLink.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLink.java
index 3617dc52f9..487022e86e 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLink.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLink.java
@@ -27,6 +27,7 @@ public class AssetLink {
   private String linkLabel;
   private String queryHint;
   private boolean editingDisabled;
+  private boolean navigationActive;
 
   public AssetLink() {
   }
@@ -71,6 +72,14 @@ public class AssetLink {
     this.queryHint = queryHint;
   }
 
+  public boolean isNavigationActive() {
+    return navigationActive;
+  }
+
+  public void setNavigationActive(boolean navigationActive) {
+    this.navigationActive = navigationActive;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLocation.java
similarity index 55%
copy from 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLocation.java
index 0140eafdf5..9cde68d157 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetLocation.java
@@ -18,37 +18,5 @@
 
 package org.apache.streampipes.model.assets;
 
-import org.apache.streampipes.commons.constants.GenericDocTypes;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.gson.annotations.SerializedName;
-
-public class SpAssetModel extends SpAsset {
-
-  public static final String APP_DOC_TYPE = 
GenericDocTypes.DOC_ASSET_MANGEMENT;
-
-  @JsonProperty("_id")
-  private @SerializedName("_id") String id;
-
-  private boolean removable;
-
-  public SpAssetModel() {
-    super();
-  }
-
-  public String getId() {
-    return id;
-  }
-
-  public void setId(String id) {
-    this.id = id;
-  }
-
-  public boolean isRemovable() {
-    return removable;
-  }
-
-  public void setRemovable(boolean removable) {
-    this.removable = removable;
-  }
-}
+public record AssetLocation(LatLng coordinates,
+                            int zoom) {}
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetSite.java
similarity index 55%
copy from 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetSite.java
index 0140eafdf5..85fc494966 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetSite.java
@@ -18,37 +18,7 @@
 
 package org.apache.streampipes.model.assets;
 
-import org.apache.streampipes.commons.constants.GenericDocTypes;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.gson.annotations.SerializedName;
-
-public class SpAssetModel extends SpAsset {
-
-  public static final String APP_DOC_TYPE = 
GenericDocTypes.DOC_ASSET_MANGEMENT;
-
-  @JsonProperty("_id")
-  private @SerializedName("_id") String id;
-
-  private boolean removable;
-
-  public SpAssetModel() {
-    super();
-  }
-
-  public String getId() {
-    return id;
-  }
-
-  public void setId(String id) {
-    this.id = id;
-  }
-
-  public boolean isRemovable() {
-    return removable;
-  }
-
-  public void setRemovable(boolean removable) {
-    this.removable = removable;
-  }
-}
+public record AssetSite(String siteId,
+                        String area,
+                        boolean hasExactLocation,
+                        AssetLocation location) {}
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetType.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetType.java
index 45b7151cf9..3394c078e7 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetType.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/AssetType.java
@@ -24,6 +24,7 @@ public class AssetType {
   private String assetIconColor;
   private String assetTypeCategory;
   private String assetTypeLabel;
+  private Isa95Type isa95AssetType;
 
   public AssetType() {
   }
@@ -59,4 +60,12 @@ public class AssetType {
   public void setAssetTypeLabel(String assetTypeLabel) {
     this.assetTypeLabel = assetTypeLabel;
   }
+
+  public Isa95Type getIsa95AssetType() {
+    return isa95AssetType;
+  }
+
+  public void setIsa95AssetType(Isa95Type isa95AssetType) {
+    this.isa95AssetType = isa95AssetType;
+  }
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/Isa95Type.java
similarity index 55%
copy from 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/Isa95Type.java
index 0140eafdf5..f4cb6a394d 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/Isa95Type.java
@@ -18,37 +18,13 @@
 
 package org.apache.streampipes.model.assets;
 
-import org.apache.streampipes.commons.constants.GenericDocTypes;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.gson.annotations.SerializedName;
-
-public class SpAssetModel extends SpAsset {
-
-  public static final String APP_DOC_TYPE = 
GenericDocTypes.DOC_ASSET_MANGEMENT;
-
-  @JsonProperty("_id")
-  private @SerializedName("_id") String id;
-
-  private boolean removable;
-
-  public SpAssetModel() {
-    super();
-  }
-
-  public String getId() {
-    return id;
-  }
-
-  public void setId(String id) {
-    this.id = id;
-  }
-
-  public boolean isRemovable() {
-    return removable;
-  }
-
-  public void setRemovable(boolean removable) {
-    this.removable = removable;
-  }
+public enum Isa95Type {
+  PROCESS_CELL,
+  PRODUCTION_UNIT,
+  PRODUCTION_LINE,
+  STORAGE_ZONE,
+  UNIT,
+  WORK_CELL,
+  STORAGE_UNIT,
+  OTHER
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/LatLng.java
similarity index 55%
copy from 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/assets/LatLng.java
index 0140eafdf5..e71016d964 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/LatLng.java
@@ -18,37 +18,6 @@
 
 package org.apache.streampipes.model.assets;
 
-import org.apache.streampipes.commons.constants.GenericDocTypes;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.gson.annotations.SerializedName;
-
-public class SpAssetModel extends SpAsset {
-
-  public static final String APP_DOC_TYPE = 
GenericDocTypes.DOC_ASSET_MANGEMENT;
-
-  @JsonProperty("_id")
-  private @SerializedName("_id") String id;
-
-  private boolean removable;
-
-  public SpAssetModel() {
-    super();
-  }
-
-  public String getId() {
-    return id;
-  }
-
-  public void setId(String id) {
-    this.id = id;
-  }
-
-  public boolean isRemovable() {
-    return removable;
-  }
-
-  public void setRemovable(boolean removable) {
-    this.removable = removable;
-  }
+public record LatLng(double latitude,
+                     double longitude) {
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAsset.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAsset.java
index eeee4c670d..66972b1129 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAsset.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAsset.java
@@ -19,7 +19,11 @@
 package org.apache.streampipes.model.assets;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class SpAsset {
 
@@ -30,11 +34,16 @@ public class SpAsset {
   private AssetType assetType;
   private List<AssetLink> assetLinks;
 
+  private AssetSite assetSite;
   private List<SpAsset> assets;
+  private Set<String> labelIds;
+
+  private Map<String, Object> additionalData = new HashMap<>();
 
   public SpAsset() {
     this.assets = new ArrayList<>();
     this.assetLinks = new ArrayList<>();
+    this.labelIds = new HashSet<>();
   }
 
   public String getAssetId() {
@@ -84,4 +93,28 @@ public class SpAsset {
   public void setAssets(List<SpAsset> assets) {
     this.assets = assets;
   }
+
+  public void setAdditionalData(Map<String, Object> additionalData) {
+    this.additionalData = additionalData;
+  }
+
+  public Map<String, Object> getAdditionalData() {
+    return additionalData;
+  }
+
+  public Set<String> getLabelIds() {
+    return labelIds;
+  }
+
+  public void setLabelIds(Set<String> labelIds) {
+    this.labelIds = labelIds;
+  }
+
+  public AssetSite getAssetSite() {
+    return assetSite;
+  }
+
+  public void setAssetSite(AssetSite assetSite) {
+    this.assetSite = assetSite;
+  }
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
index 0140eafdf5..bcc5492db5 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/assets/SpAssetModel.java
@@ -19,16 +19,25 @@
 package org.apache.streampipes.model.assets;
 
 import org.apache.streampipes.commons.constants.GenericDocTypes;
+import org.apache.streampipes.model.shared.annotation.TsModel;
+import org.apache.streampipes.model.shared.api.Storable;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonAlias;
 import com.google.gson.annotations.SerializedName;
 
-public class SpAssetModel extends SpAsset {
+@TsModel
+public class SpAssetModel extends SpAsset implements Storable {
 
   public static final String APP_DOC_TYPE = 
GenericDocTypes.DOC_ASSET_MANGEMENT;
 
-  @JsonProperty("_id")
-  private @SerializedName("_id") String id;
+  private final String appDocType = APP_DOC_TYPE;
+
+  @JsonAlias("_id")
+  private @SerializedName("_id") String elementId;
+
+  @JsonAlias("_rev")
+  @SerializedName("_rev")
+  private String rev;
 
   private boolean removable;
 
@@ -36,14 +45,6 @@ public class SpAssetModel extends SpAsset {
     super();
   }
 
-  public String getId() {
-    return id;
-  }
-
-  public void setId(String id) {
-    this.id = id;
-  }
-
   public boolean isRemovable() {
     return removable;
   }
@@ -51,4 +52,28 @@ public class SpAssetModel extends SpAsset {
   public void setRemovable(boolean removable) {
     this.removable = removable;
   }
+
+  @Override
+  public String getRev() {
+    return rev;
+  }
+
+  @Override
+  public void setRev(String rev) {
+    this.rev = rev;
+  }
+
+  @Override
+  public String getElementId() {
+    return elementId;
+  }
+
+  @Override
+  public void setElementId(String elementId) {
+    this.elementId = elementId;
+  }
+
+  public String getAppDocType() {
+    return appDocType;
+  }
 }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
index 1e627973c6..d3d8953746 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.manager.setup;
 
 import org.apache.streampipes.manager.setup.design.UserDesignDocument;
+import org.apache.streampipes.manager.setup.tasks.AddAssetManagementViewTask;
 import org.apache.streampipes.manager.setup.tasks.AddDataLakeMeasureViewTask;
 import 
org.apache.streampipes.manager.setup.tasks.AddDefaultPipelineTemplatesTask;
 import org.apache.streampipes.manager.setup.tasks.CreateAssetLinkTypeTask;
@@ -79,6 +80,7 @@ public class CouchDbInstallationStep extends InstallationStep 
{
     addNotificationView();
     addPipelineView();
     addDataLakeMeasureView();
+    addAssetManagementView();
   }
 
   private void addNotificationView() {
@@ -174,4 +176,8 @@ public class CouchDbInstallationStep extends 
InstallationStep {
   private void addDataLakeMeasureView() {
     new AddDataLakeMeasureViewTask().execute();
   }
+
+  private void addAssetManagementView() {
+    new AddAssetManagementViewTask().execute();
+  }
 }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddAssetManagementViewTask.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddAssetManagementViewTask.java
new file mode 100644
index 0000000000..b724bcc957
--- /dev/null
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddAssetManagementViewTask.java
@@ -0,0 +1,48 @@
+/*
+ * 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.manager.setup.tasks;
+
+import org.apache.streampipes.storage.couchdb.utils.Utils;
+
+import org.lightcouch.DesignDocument;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static 
org.apache.streampipes.manager.setup.design.DesignDocumentUtils.prepareDocument;
+
+public class AddAssetManagementViewTask implements InstallationTask {
+  public static final String DESIGN_DOCUMENT = "_design/assets";
+
+  public static final String VIEW_NAME = "all-assets";
+  public static final String MAP_FUNCTION = "function(doc) { if(doc.appDocType 
=== 'asset-management') { emit(doc._id, doc); } }";
+
+  @Override
+  public void execute() {
+    DesignDocument dataLakeDoc = prepareDocument(DESIGN_DOCUMENT);
+    Map<String, DesignDocument.MapReduce> views = new HashMap<>();
+
+    DesignDocument.MapReduce byNameFn = new DesignDocument.MapReduce();
+    byNameFn.setMap(MAP_FUNCTION);
+
+    views.put(VIEW_NAME, byNameFn);
+    dataLakeDoc.setViews(views);
+    Utils.getCouchDbClient("genericstorage", 
true).design().synchronizeWithDb(dataLakeDoc);
+  }
+}
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
index 9293592f65..0e1fa185fe 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
@@ -29,7 +29,7 @@ public class CreateDefaultAssetTask implements 
InstallationTask {
   @Override
   public void execute() {
     var asset = new SpAssetModel();
-    asset.setId(GenericDocTypes.DEFAULT_ASSET_DOC_ID);
+    asset.setElementId(GenericDocTypes.DEFAULT_ASSET_DOC_ID);
     asset.setAssetId("default-asset");
     asset.setAssetName("Default Asset");
     asset.setRemovable(true);
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetManagementResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetManagementResource.java
index e4599211d3..6099f9d13d 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetManagementResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetManagementResource.java
@@ -18,15 +18,14 @@
 
 package org.apache.streampipes.rest.impl;
 
+import org.apache.streampipes.model.assets.SpAssetModel;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 import org.apache.streampipes.rest.security.AuthConstants;
-import org.apache.streampipes.rest.shared.exception.SpMessageException;
-import org.apache.streampipes.storage.api.IGenericStorage;
+import org.apache.streampipes.storage.api.CRUDStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -39,9 +38,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import java.io.IOException;
 import java.util.List;
-import java.util.Map;
 
 @RestController
 @RequestMapping("/api/v2/assets")
@@ -49,66 +46,49 @@ public class AssetManagementResource extends 
AbstractAuthGuardedRestResource {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(AssetManagementResource.class);
 
-  private static final String APP_DOC_TYPE = "asset-management";
+  private final CRUDStorage<SpAssetModel> assetStorage;
+
+  public AssetManagementResource() {
+    this.assetStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getAssetStorage();
+  }
 
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_READ_ASSETS_PRIVILEGE)
-  public List<Map<String, Object>> getAll() throws IOException {
-    return getGenericStorage().findAll(APP_DOC_TYPE);
+  public List<SpAssetModel> getAll() {
+    return assetStorage.findAll();
   }
 
   @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = 
MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_WRITE_ASSETS_PRIVILEGE)
-  public ResponseEntity<?> create(@RequestBody String asset) {
-    try {
-      Map<String, Object> obj = getGenericStorage().create(asset);
-      return ok(obj);
-    } catch (IOException e) {
-      LOG.error("Could not connect to storage", e);
-      return fail();
-    }
+  public ResponseEntity<?> create(@RequestBody SpAssetModel asset) {
+    assetStorage.persist(asset);
+    return ok();
   }
 
   @GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_READ_ASSETS_PRIVILEGE)
-  public ResponseEntity<Map<String, Object>> getCategory(@PathVariable("id") 
String assetId) {
-    try {
-      Map<String, Object> obj = getGenericStorage().findOne(assetId);
-      return ok(obj);
-    } catch (IOException e) {
-      LOG.error("Could not connect to storage", e);
-      throw new SpMessageException(HttpStatus.INTERNAL_SERVER_ERROR, e);
-    }
+  public ResponseEntity<SpAssetModel> getAsset(@PathVariable("id") String 
elementId) {
+      var obj = assetStorage.getElementById(elementId);
+      if (obj != null) {
+        return ok(obj);
+      } else {
+        return null;
+      }
   }
 
   @PutMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE, 
consumes = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_WRITE_ASSETS_PRIVILEGE)
-  public ResponseEntity<Map<String, Object>> update(@PathVariable("id") String 
assetId,
-      @RequestBody String asset) {
-    try {
-      Map<String, Object> obj = getGenericStorage().update(assetId, asset);
-      return ok(obj);
-    } catch (IOException e) {
-      LOG.error("Could not connect to storage", e);
-      throw new SpMessageException(HttpStatus.INTERNAL_SERVER_ERROR, e);
-    }
+  public ResponseEntity<SpAssetModel> update(@PathVariable("id") String 
assetId,
+      @RequestBody SpAssetModel asset) {
+    var obj = assetStorage.updateElement(asset);
+    return ok(obj);
   }
 
-  @DeleteMapping(path = "/{id}/{rev}", produces = 
MediaType.APPLICATION_JSON_VALUE)
+  @DeleteMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_WRITE_ASSETS_PRIVILEGE)
-  public ResponseEntity<Void> delete(@PathVariable("id") String assetId,
-      @PathVariable("rev") String rev) {
-    try {
-      getGenericStorage().delete(assetId, rev);
-      return ok();
-    } catch (IOException e) {
-      LOG.error("Could not connect to storage", e);
-      throw new SpMessageException(HttpStatus.INTERNAL_SERVER_ERROR, e);
-    }
+  public ResponseEntity<Void> delete(@PathVariable("id") String assetId) {
+    var asset = assetStorage.getElementById(assetId);
+    assetStorage.deleteElement(asset);
+    return ok();
   }
-
-  private IGenericStorage getGenericStorage() {
-    return StorageDispatcher.INSTANCE.getNoSqlStore().getGenericStorage();
-  }
-
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index b4afb1897a..47c3b01254 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -31,6 +31,8 @@ import 
org.apache.streampipes.service.core.migrations.v0980.AddDefaultExportProv
 import 
org.apache.streampipes.service.core.migrations.v0980.FixImportedPermissionsMigration;
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinkTypesMigration;
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinksMigration;
+import 
org.apache.streampipes.service.core.migrations.v099.AddAssetManagementViewMigration;
+import 
org.apache.streampipes.service.core.migrations.v099.MoveAssetContentMigration;
 import 
org.apache.streampipes.service.core.migrations.v970.AddDataLakePipelineTemplateMigration;
 import 
org.apache.streampipes.service.core.migrations.v970.AddLinkSettingsMigration;
 import 
org.apache.streampipes.service.core.migrations.v970.AddRolesToUserDbMigration;
@@ -62,7 +64,9 @@ public class AvailableMigrations {
         new ModifyAssetLinkTypesMigration(),
         new AddDataLakeMeasureViewMigration(),
         new AddDefaultExportProviderMigration(),
-        new FixImportedPermissionsMigration()
+        new FixImportedPermissionsMigration(),
+        new AddAssetManagementViewMigration(),
+        new MoveAssetContentMigration()
     );
   }
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/FixImportedPermissionsMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/FixImportedPermissionsMigration.java
index 0a0961801c..ad5fb362ce 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/FixImportedPermissionsMigration.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/FixImportedPermissionsMigration.java
@@ -90,17 +90,19 @@ public class FixImportedPermissionsMigration implements 
Migration {
     resources.forEach(resource -> {
       // This uses the same sanitization logic as the exporter
       var sanitizedId = sanitize(resource.getElementId());
-      var permissions = 
permissionStorage.getUserPermissionsForObject(sanitizedId);
-
-      permissions.forEach(permission -> {
-        permission.setObjectInstanceId(resource.getElementId());
-        permissionStorage.updateElement(permission);
-        LOG.info(
-            "Updated permission id from {} to {}",
-            sanitizedId,
-            resource.getElementId()
-        );
-      });
+      if (!sanitizedId.equals(resource.getElementId())) {
+        var permissions = 
permissionStorage.getUserPermissionsForObject(sanitizedId);
+
+        permissions.forEach(permission -> {
+          permission.setObjectInstanceId(resource.getElementId());
+          permissionStorage.updateElement(permission);
+          LOG.info(
+              "Updated permission id from {} to {}",
+              sanitizedId,
+              resource.getElementId()
+          );
+        });
+      }
     });
   }
 
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/AddAssetManagementViewMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/AddAssetManagementViewMigration.java
new file mode 100644
index 0000000000..915674c006
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/AddAssetManagementViewMigration.java
@@ -0,0 +1,52 @@
+/*
+ * 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.service.core.migrations.v099;
+
+import org.apache.streampipes.manager.setup.tasks.AddAssetManagementViewTask;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.couchdb.utils.Utils;
+
+import org.lightcouch.NoDocumentException;
+
+import java.io.IOException;
+
+public class AddAssetManagementViewMigration implements Migration {
+
+  @Override
+  public boolean shouldExecute() {
+    var client = Utils.getCouchDbClient("genericstorage", true);
+    try {
+      var designDoc = 
client.design().getFromDb(AddAssetManagementViewTask.DESIGN_DOCUMENT);
+
+      return designDoc == null || 
designDoc.getViews().containsKey(AddAssetManagementViewTask.VIEW_NAME);
+    } catch (NoDocumentException e) {
+      return true;
+    }
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    new AddAssetManagementViewTask().execute();
+  }
+
+  @Override
+  public String getDescription() {
+    return "Adding design document and view for managing assets";
+  }
+}
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/MoveAssetContentMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/MoveAssetContentMigration.java
new file mode 100644
index 0000000000..b90b71fb98
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/MoveAssetContentMigration.java
@@ -0,0 +1,120 @@
+/*
+ * 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.service.core.migrations.v099;
+
+import org.apache.streampipes.model.assets.SpAssetModel;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.api.IGenericStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class MoveAssetContentMigration implements Migration {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(MoveAssetContentMigration.class);
+
+  private IGenericStorage genericStorage;
+  private final ObjectMapper mapper = new ObjectMapper();
+
+  private static final Set<String> allowedKeys = Set.of(
+      "assetId",
+      "assetName",
+      "assetDescription",
+      "assetType",
+      "assetLinks",
+      "assetSite",
+      "assets",
+      "_id",
+      "_rev",
+      "removable",
+      "additionalData",
+      "labelIds",
+      "appDocType"
+  );
+
+  public MoveAssetContentMigration() {
+    this.genericStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getGenericStorage();
+  }
+
+  @Override
+  public boolean shouldExecute() {
+    return true;
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    var allAssets = genericStorage.findAll(SpAssetModel.APP_DOC_TYPE);
+    allAssets.forEach(a -> {
+      try {
+        migrate(a);
+        genericStorage.update(a.get("_id").toString(), 
mapper.writeValueAsString(a));
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    });
+
+  }
+
+  @Override
+  public String getDescription() {
+    return "Move optional asset properties to additionalProperties";
+  }
+
+  private void migrate(Map<String, Object> asset) {
+    var additionalProperties = (Map<String, Object>) asset.computeIfAbsent(
+        "additionalData",
+        key -> new HashMap<String, Object>()
+    );
+
+    // Move non-allowed keys (except "assets" and "additionalProperties" 
itself)
+    var iterator = asset.entrySet().iterator();
+    while (iterator.hasNext()) {
+      var entry = iterator.next();
+      var key = entry.getKey();
+
+      if ("assets".equals(key) || "additionalData".equals(key)) {
+        continue;
+      }
+
+      if (!allowedKeys.contains(key)) {
+        additionalProperties.put(key, entry.getValue());
+        iterator.remove();
+      }
+    }
+
+    var assetsValue = asset.get("assets");
+    if (assetsValue instanceof List<?> assets && !assets.isEmpty()) {
+      for (var element : assets) {
+        if (element instanceof Map<?, ?> childMap) {
+          migrate((Map<String, Object>) childMap);
+        } else {
+          LOG.warn("Unexpected element type in 'assets': {}", 
element.getClass());
+        }
+      }
+    }
+  }
+}
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
index 3f130bbb02..96b0c02756 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
+++ 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.storage.api;
 
+import org.apache.streampipes.model.assets.SpAssetModel;
 import org.apache.streampipes.model.client.user.Group;
 import org.apache.streampipes.model.client.user.PasswordRecoveryToken;
 import org.apache.streampipes.model.client.user.Privilege;
@@ -87,4 +88,6 @@ public interface INoSqlStorage {
   CRUDStorage<CompactPipelineTemplate> getPipelineTemplateStorage();
 
   CRUDStorage<Certificate> getCertificateStorage();
+
+  CRUDStorage<SpAssetModel> getAssetStorage();
 }
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
index c669121a05..1ad029641f 100644
--- 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.storage.couchdb;
 
+import org.apache.streampipes.model.assets.SpAssetModel;
 import org.apache.streampipes.model.client.user.Group;
 import org.apache.streampipes.model.client.user.PasswordRecoveryToken;
 import org.apache.streampipes.model.client.user.Privilege;
@@ -49,6 +50,7 @@ import 
org.apache.streampipes.storage.api.ISpCoreConfigurationStorage;
 import org.apache.streampipes.storage.api.IUserStorage;
 import 
org.apache.streampipes.storage.couchdb.impl.AdapterDescriptionStorageImpl;
 import org.apache.streampipes.storage.couchdb.impl.AdapterInstanceStorageImpl;
+import org.apache.streampipes.storage.couchdb.impl.AssetStorageImpl;
 import 
org.apache.streampipes.storage.couchdb.impl.CoreConfigurationStorageImpl;
 import org.apache.streampipes.storage.couchdb.impl.DataLakeMeasureStorage;
 import org.apache.streampipes.storage.couchdb.impl.DataProcessorStorageImpl;
@@ -248,4 +250,13 @@ public enum CouchDbStorageManager implements INoSqlStorage 
{
         Certificate.class
     );
   }
+
+  @Override
+  public CRUDStorage<SpAssetModel> getAssetStorage() {
+    return new AssetStorageImpl(
+        () -> Utils.getCouchDbGsonClient("genericstorage")
+    );
+  }
+
+
 }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/AssetStorageImpl.java
similarity index 55%
copy from 
streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
copy to 
streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/AssetStorageImpl.java
index 9293592f65..3b1f39d68f 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/CreateDefaultAssetTask.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/AssetStorageImpl.java
@@ -16,28 +16,17 @@
  *
  */
 
-package org.apache.streampipes.manager.setup.tasks;
+package org.apache.streampipes.storage.couchdb.impl;
 
-import org.apache.streampipes.commons.constants.GenericDocTypes;
 import org.apache.streampipes.model.assets.SpAssetModel;
-import org.apache.streampipes.storage.management.StorageDispatcher;
 
-import java.io.IOException;
+import org.lightcouch.CouchDbClient;
 
-public class CreateDefaultAssetTask implements InstallationTask {
+import java.util.function.Supplier;
 
-  @Override
-  public void execute() {
-    var asset = new SpAssetModel();
-    asset.setId(GenericDocTypes.DEFAULT_ASSET_DOC_ID);
-    asset.setAssetId("default-asset");
-    asset.setAssetName("Default Asset");
-    asset.setRemovable(true);
+public class AssetStorageImpl extends DefaultViewCrudStorage<SpAssetModel> {
 
-    try {
-      
StorageDispatcher.INSTANCE.getNoSqlStore().getGenericStorage().create(asset, 
SpAssetModel.class);
-    } catch (IOException e) {
-      e.printStackTrace();
-    }
+  public AssetStorageImpl(Supplier<CouchDbClient> clientSupplier) {
+    super(clientSupplier, SpAssetModel.class, "assets/all-assets");
   }
 }
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/apis/asset-management.service.ts
 
b/ui/projects/streampipes/platform-services/src/lib/apis/asset-management.service.ts
index 621ebe0bed..2d1312157b 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/apis/asset-management.service.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/apis/asset-management.service.ts
@@ -20,6 +20,7 @@ import { inject, Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { PlatformServicesCommons } from './commons.service';
 import { Observable } from 'rxjs';
+import { SpAssetModel } from '../model/gen/streampipes-model';
 
 @Injectable({
     providedIn: 'root',
@@ -28,24 +29,24 @@ export class AssetManagementService {
     private http = inject(HttpClient);
     private platformServicesCommons = inject(PlatformServicesCommons);
 
-    createAsset(asset: any): Observable<any> {
+    createAsset(asset: SpAssetModel): Observable<any> {
         return this.http.post(this.assetBasePath, asset);
     }
 
-    getAllAssets(): Observable<any> {
-        return this.http.get(this.assetBasePath);
+    getAllAssets(): Observable<SpAssetModel[]> {
+        return this.http.get<SpAssetModel[]>(this.assetBasePath);
     }
 
-    getAsset(assetId: string): Observable<any> {
-        return this.http.get(`${this.assetBasePath}/${assetId}`);
+    getAsset(assetId: string): Observable<SpAssetModel> {
+        return this.http.get<SpAssetModel>(`${this.assetBasePath}/${assetId}`);
     }
 
-    updateAsset(asset: any): Observable<any> {
-        return this.http.put(`${this.assetBasePath}/${asset._id}`, asset);
+    updateAsset(asset: SpAssetModel): Observable<any> {
+        return this.http.put(`${this.assetBasePath}/${asset.elementId}`, 
asset);
     }
 
-    deleteAsset(assetId: string, rev: string): Observable<any> {
-        return this.http.delete(`${this.assetBasePath}/${assetId}/${rev}`);
+    deleteAsset(assetId: string): Observable<any> {
+        return this.http.delete(`${this.assetBasePath}/${assetId}`);
     }
 
     private get assetBasePath() {
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts 
b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
index e78b42fbb4..8276c958f9 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
@@ -16,6 +16,8 @@
  *
  */
 
+import { AssetLink, AssetLocation, Isa95Type } from '../gen/streampipes-model';
+
 export interface AssetLinkType {
     linkType: string;
     linkLabel: string;
@@ -26,23 +28,6 @@ export interface AssetLinkType {
     navigationActive: boolean;
 }
 
-export interface AssetType {
-    assetIcon: string;
-    assetIconColor: string;
-    assetTypeCategory: string;
-    assetTypeLabel: string;
-    isa95AssetType?: Isa95Type;
-}
-
-export interface AssetLink {
-    resourceId: string;
-    linkType: 'data-view' | 'dashboard' | 'adapter' | 'source' | string;
-    linkLabel: string;
-    queryHint: string;
-    editingDisabled: boolean;
-    navigationActive: boolean;
-}
-
 export interface LinkageData {
     //Data Model to extract AssetLinks from the UI
     name: string;
@@ -56,11 +41,6 @@ export interface Isa95TypeDesc {
     type: Isa95Type;
 }
 
-export interface AssetLocation {
-    coordinates: LatLng;
-    zoom?: number;
-}
-
 export interface AssetSiteDesc {
     _id: string;
     _rev?: string;
@@ -70,38 +50,6 @@ export interface AssetSiteDesc {
     areas: string[];
 }
 
-export interface LatLng {
-    latitude: number;
-    longitude: number;
-}
-
-export interface AssetSite {
-    siteId: string;
-    area: string;
-    hasExactLocation: boolean;
-    location?: AssetLocation;
-}
-
-export interface SpAsset {
-    assetId: string;
-    assetName: string;
-    assetDescription: string;
-    assetType: AssetType;
-    labelIds?: string[];
-    assetLinks: AssetLink[];
-    assetSite?: AssetSite;
-    assets: SpAsset[];
-}
-
-export interface SpAssetModel extends SpAsset {
-    _id: string;
-    _rev: string;
-
-    appDocType: string;
-
-    removable: boolean;
-}
-
 export interface SpAssetTreeNode {
     assetId: string;
     assetName: string;
@@ -110,13 +58,3 @@ export interface SpAssetTreeNode {
     spAssetModelId: string;
     flattenPath: any[];
 }
-
-export type Isa95Type =
-    | 'PROCESS_CELL'
-    | 'PRODUCTION_UNIT'
-    | 'PRODUCTION_LINE'
-    | 'STORAGE_ZONE'
-    | 'UNIT'
-    | 'WORK_CELL'
-    | 'STORAGE_UNIT'
-    | 'OTHER';
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index 498a22c86c..de2fa3210a 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -20,7 +20,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-11-12 
10:22:54.
+// Generated using typescript-generator version 3.2.1263 on 2025-11-17 
21:13:12.
 
 export class NamedStreamPipesEntity implements Storable {
     '@class':
@@ -652,6 +652,87 @@ export class AssetExportConfiguration {
     }
 }
 
+export class AssetLink {
+    editingDisabled: boolean;
+    linkLabel: string;
+    linkType: string;
+    navigationActive: boolean;
+    queryHint: string;
+    resourceId: string;
+
+    static fromData(data: AssetLink, target?: AssetLink): AssetLink {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new AssetLink();
+        instance.editingDisabled = data.editingDisabled;
+        instance.linkLabel = data.linkLabel;
+        instance.linkType = data.linkType;
+        instance.navigationActive = data.navigationActive;
+        instance.queryHint = data.queryHint;
+        instance.resourceId = data.resourceId;
+        return instance;
+    }
+}
+
+export class AssetLocation {
+    coordinates: LatLng;
+    zoom: number;
+
+    static fromData(
+        data: AssetLocation,
+        target?: AssetLocation,
+    ): AssetLocation {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new AssetLocation();
+        instance.coordinates = LatLng.fromData(data.coordinates);
+        instance.zoom = data.zoom;
+        return instance;
+    }
+}
+
+export class AssetSite {
+    area: string;
+    hasExactLocation: boolean;
+    location: AssetLocation;
+    siteId: string;
+
+    static fromData(data: AssetSite, target?: AssetSite): AssetSite {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new AssetSite();
+        instance.area = data.area;
+        instance.hasExactLocation = data.hasExactLocation;
+        instance.location = AssetLocation.fromData(data.location);
+        instance.siteId = data.siteId;
+        return instance;
+    }
+}
+
+export class AssetType {
+    assetIcon: string;
+    assetIconColor: string;
+    assetTypeCategory: string;
+    assetTypeLabel: string;
+    isa95AssetType: Isa95Type;
+
+    static fromData(data: AssetType, target?: AssetType): AssetType {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new AssetType();
+        instance.assetIcon = data.assetIcon;
+        instance.assetIconColor = data.assetIconColor;
+        instance.assetTypeCategory = data.assetTypeCategory;
+        instance.assetTypeLabel = data.assetTypeLabel;
+        instance.isa95AssetType = data.isa95AssetType;
+        return instance;
+    }
+}
+
 export class CanvasPosition {
     x: number;
     y: number;
@@ -2298,6 +2379,21 @@ export class KeepOutputStrategy extends OutputStrategy {
     }
 }
 
+export class LatLng {
+    latitude: number;
+    longitude: number;
+
+    static fromData(data: LatLng, target?: LatLng): LatLng {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new LatLng();
+        instance.latitude = data.latitude;
+        instance.longitude = data.longitude;
+        return instance;
+    }
+}
+
 export class LinkSettings {
     documentationUrl: string;
     showApiDocumentationLinkOnStartScreen: boolean;
@@ -3706,6 +3802,61 @@ export class SlideToggleStaticProperty extends 
StaticProperty {
     }
 }
 
+export class SpAsset {
+    additionalData: { [index: string]: any };
+    assetDescription: string;
+    assetId: string;
+    assetLinks: AssetLink[];
+    assetName: string;
+    assetSite: AssetSite;
+    assetType: AssetType;
+    assets: SpAsset[];
+    labelIds: string[];
+
+    static fromData(data: SpAsset, target?: SpAsset): SpAsset {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new SpAsset();
+        instance.additionalData = __getCopyObjectFn(__identity<any>())(
+            data.additionalData,
+        );
+        instance.assetDescription = data.assetDescription;
+        instance.assetId = data.assetId;
+        instance.assetLinks = __getCopyArrayFn(AssetLink.fromData)(
+            data.assetLinks,
+        );
+        instance.assetName = data.assetName;
+        instance.assetSite = AssetSite.fromData(data.assetSite);
+        instance.assetType = AssetType.fromData(data.assetType);
+        instance.assets = __getCopyArrayFn(SpAsset.fromData)(data.assets);
+        instance.labelIds = __getCopyArrayFn(__identity<string>())(
+            data.labelIds,
+        );
+        return instance;
+    }
+}
+
+export class SpAssetModel extends SpAsset implements Storable {
+    appDocType: string;
+    elementId: string;
+    removable: boolean;
+    rev: string;
+
+    static fromData(data: SpAssetModel, target?: SpAssetModel): SpAssetModel {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new SpAssetModel();
+        super.fromData(data, instance);
+        instance.appDocType = data.appDocType;
+        instance.elementId = data.elementId;
+        instance.removable = data.removable;
+        instance.rev = data.rev;
+        return instance;
+    }
+}
+
 export class SpDataStream extends NamedStreamPipesEntity {
     '@class': 'org.apache.streampipes.model.SpDataStream';
     'category': string[];
@@ -4307,6 +4458,16 @@ export type EventPropertyUnion =
 
 export type FieldStatus = 'GOOD' | 'BAD' | 'ATTENTION';
 
+export type Isa95Type =
+    | 'PROCESS_CELL'
+    | 'PRODUCTION_UNIT'
+    | 'PRODUCTION_LINE'
+    | 'STORAGE_ZONE'
+    | 'UNIT'
+    | 'WORK_CELL'
+    | 'STORAGE_UNIT'
+    | 'OTHER';
+
 export type MappingPropertyUnion = MappingPropertyNary | MappingPropertyUnary;
 
 export type OneOfStaticPropertyUnion = RuntimeResolvableOneOfStaticProperty;
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/services/isa95-type.service.ts
 
b/ui/projects/streampipes/platform-services/src/lib/services/isa95-type.service.ts
index c291aa8f69..f9a0afbdaa 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/services/isa95-type.service.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/services/isa95-type.service.ts
@@ -17,7 +17,8 @@
  */
 
 import { Injectable } from '@angular/core';
-import { Isa95Type, Isa95TypeDesc } from '../model/assets/asset.model';
+import { Isa95TypeDesc } from '../model/assets/asset.model';
+import { Isa95Type } from '../model/gen/streampipes-model';
 
 @Injectable({ providedIn: 'root' })
 export class Isa95TypeService {
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-hierarchy/asset-browser-hierarchy.component.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-hierarchy/asset-browser-hierarchy.component.ts
index 6ed2f52254..217bd99f9e 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-hierarchy/asset-browser-hierarchy.component.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser-hierarchy/asset-browser-hierarchy.component.ts
@@ -19,12 +19,12 @@
 import {
     Component,
     EventEmitter,
+    inject,
     Input,
     OnChanges,
     Output,
     SimpleChanges,
     ViewChild,
-    inject,
 } from '@angular/core';
 import { AssetBrowserData } from '../asset-browser.model';
 import { NestedTreeControl } from '@angular/cdk/tree';
@@ -107,6 +107,9 @@ export class AssetBrowserHierarchyComponent implements 
OnChanges {
             assetLinks: [],
             assets: this.makeAssets(),
             assetType: undefined,
+            assetSite: undefined,
+            additionalData: {},
+            labelIds: [],
         };
     }
 
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts
index 8732e87579..07b4484af0 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-browser/asset-browser.service.ts
@@ -16,10 +16,11 @@
  *
  */
 
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import { BehaviorSubject, zip } from 'rxjs';
 import {
     AssetConstants,
+    AssetManagementService,
     AssetSiteDesc,
     GenericStorageService,
     Isa95TypeDesc,
@@ -46,10 +47,11 @@ export class SpAssetBrowserService {
     loadedAssetData: AssetBrowserData;
     activeAssetLink = undefined;
 
-    constructor(
-        private genericStorageService: GenericStorageService,
-        private typeService: Isa95TypeService,
-    ) {
+    private genericStorageService = inject(GenericStorageService);
+    private typeService = inject(Isa95TypeService);
+    private assetService = inject(AssetManagementService);
+
+    constructor() {
         this.loadAssetData();
     }
 
@@ -61,9 +63,7 @@ export class SpAssetBrowserService {
     }
 
     loadAssetData(): void {
-        const assets$ = this.genericStorageService.getAllDocuments(
-            AssetConstants.ASSET_APP_DOC_NAME,
-        );
+        const assets$ = this.assetService.getAllAssets();
         const assetLinks$ = this.genericStorageService.getAllDocuments(
             AssetConstants.ASSET_LINK_TYPES_DOC_NAME,
         );
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-link-configuration/asset-link-configuration.component.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-link-configuration/asset-link-configuration.component.ts
index 84f55d2530..5a48064ebb 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/asset-link-configuration/asset-link-configuration.component.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/asset-link-configuration/asset-link-configuration.component.ts
@@ -16,19 +16,17 @@
  *
  */
 
-import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import { NestedTreeControl } from '@angular/cdk/tree';
 import { MatTreeNestedDataSource } from '@angular/material/tree';
 import {
+    AssetLinkType,
     AssetManagementService,
     LinkageData,
-    SpAssetModel,
-    AssetLinkType,
     SpAsset,
     SpAssetTreeNode,
 } from '@streampipes/platform-services';
 import { MatStepper } from '@angular/material/stepper';
-import { Observable } from 'rxjs';
 
 @Component({
     selector: 'sp-asset-link-configuration',
@@ -55,10 +53,7 @@ export class AssetLinkConfigurationComponent implements 
OnInit {
     treeDropdownOpen = false;
 
     assetsData: SpAssetTreeNode[] = [];
-    currentAsset: SpAssetModel;
     assetLinkTypes: AssetLinkType[] = [];
-    assetLinksLoaded = false;
-    updateObservable: Observable<SpAssetModel>;
     selectedAssets: SpAssetTreeNode[] = [];
     deselectedAssets: SpAssetTreeNode[] = [];
     originalAssets: SpAssetTreeNode[] = [];
@@ -73,10 +68,6 @@ export class AssetLinkConfigurationComponent implements 
OnInit {
     hasChild = (_: number, node: any) =>
         !!node.assets && node.assets.length > 0;
 
-    toggleTreeDropdown() {
-        this.treeDropdownOpen = !this.treeDropdownOpen;
-    }
-
     onAssetSelect(node: SpAssetTreeNode): void {
         const index = this.selectedAssets.findIndex(
             asset => asset.assetId === node.assetId,
@@ -184,8 +175,8 @@ export class AssetLinkConfigurationComponent implements 
OnInit {
             const currentPath = [...index, assetIndex];
             let flattenedPath = [];
 
-            if (asset['_id']) {
-                parentId = asset['_id'];
+            if (asset['elementId']) {
+                parentId = asset['elementId'];
                 flattenedPath = [parentId, ...currentPath];
             } else {
                 flattenedPath = [...currentPath];
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/services/asset-configuration.service.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/services/asset-configuration.service.ts
index c7dbe9ef9a..eafa6aeeca 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/services/asset-configuration.service.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/services/asset-configuration.service.ts
@@ -16,15 +16,15 @@
  *
  */
 
-import { Injectable, Output, EventEmitter } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import {
     AssetConstants,
-    AssetManagementService,
     AssetLink,
-    LinkageData,
-    SpAssetModel,
     AssetLinkType,
+    AssetManagementService,
     GenericStorageService,
+    LinkageData,
+    SpAssetModel,
     SpAssetTreeNode,
 } from '@streampipes/platform-services';
 import { firstValueFrom } from 'rxjs';
@@ -35,16 +35,14 @@ import { firstValueFrom } from 'rxjs';
 export class AssetSaveService {
     assetLinkTypes: AssetLinkType[] = [];
     currentAsset: SpAssetModel;
-    constructor(
-        private assetService: AssetManagementService,
-        private storageService: GenericStorageService,
-    ) {
+
+    private assetService = inject(AssetManagementService);
+    private storageService = inject(GenericStorageService);
+
+    constructor() {
         this.loadAssetLinkTypes();
     }
 
-    @Output() adapterStartedEmitter: EventEmitter<void> =
-        new EventEmitter<void>();
-
     async saveSelectedAssets(
         selectedAssets: SpAssetTreeNode[],
         linkageData: LinkageData[],
@@ -52,7 +50,6 @@ export class AssetSaveService {
         originalAssets: SpAssetTreeNode[] = [],
     ): Promise<void> {
         const links = this.buildLinks(linkageData);
-
         if (deselectedAssets.length > 0) {
             await this.deleteLinkOnDeselectAssets(deselectedAssets, links);
         }
@@ -73,6 +70,7 @@ export class AssetSaveService {
             }
         }
     }
+
     private filterAssets(
         originalAssets: SpAssetTreeNode[],
         deselectedAssets: SpAssetTreeNode[],
@@ -119,7 +117,7 @@ export class AssetSaveService {
                         if (path.length > 2) {
                             links.forEach(linkToUpdate => {
                                 this.updateLinkLabelInDict(
-                                    current,
+                                    current as unknown as SpAssetTreeNode,
                                     path,
                                     linkToUpdate,
                                 );
@@ -130,11 +128,7 @@ export class AssetSaveService {
                     const updateObservable =
                         this.assetService.updateAsset(current);
 
-                    updateObservable?.subscribe({
-                        next: () => {
-                            this.adapterStartedEmitter.emit();
-                        },
-                    });
+                    updateObservable?.subscribe();
                 },
             });
         });
@@ -225,7 +219,11 @@ export class AssetSaveService {
 
                 if (path.length > 2) {
                     links.forEach(linkToRemove => {
-                        this.deleteDictValue(current, path, linkToRemove);
+                        this.deleteDictValue(
+                            current as unknown as SpAssetTreeNode,
+                            path,
+                            linkToRemove,
+                        );
                     });
                 }
             });
diff --git 
a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts 
b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
index 3bc6761f6c..d1163c3755 100644
--- a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
+++ b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
@@ -16,10 +16,11 @@
  *
  */
 
-import { Directive, OnInit } from '@angular/core';
+import { Directive, inject, OnInit } from '@angular/core';
 import { SpBreadcrumbService } from '@streampipes/shared-ui';
 import {
     AssetConstants,
+    AssetManagementService,
     AssetSiteDesc,
     GenericStorageService,
     SpAsset,
@@ -39,11 +40,10 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
 
     assetModelId: string;
 
-    constructor(
-        private breadcrumbService: SpBreadcrumbService,
-        protected genericStorageService: GenericStorageService,
-        protected route: ActivatedRoute,
-    ) {}
+    private breadcrumbService = inject(SpBreadcrumbService);
+    protected route = inject(ActivatedRoute);
+    protected assetService = inject(AssetManagementService);
+    private genericStorageService = inject(GenericStorageService);
 
     ngOnInit(): void {
         this.assetModelId = this.route.snapshot.params.assetId;
@@ -51,10 +51,7 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
     }
 
     loadResources(): void {
-        const assetReq = this.genericStorageService.getDocument(
-            AssetConstants.ASSET_APP_DOC_NAME,
-            this.assetModelId,
-        );
+        const assetReq = this.assetService.getAsset(this.assetModelId);
         const locationsReq = this.genericStorageService.getAllDocuments(
             AssetConstants.ASSET_SITES_APP_DOC_NAME,
         );
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts
index eae0980908..e3b1a40651 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts
@@ -45,6 +45,7 @@ export class AssetDetailsSiteComponent implements OnChanges {
                 area: undefined,
                 siteId: undefined,
                 hasExactLocation: false,
+                location: undefined,
             };
             if (this.sites.length > 0) {
                 if (this.asset.assetSite.siteId !== undefined) {
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
index 2f1af1d858..401a2f272a 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
@@ -16,16 +16,9 @@
  *
  */
 
-import { Component } from '@angular/core';
-import {
-    SpAssetBrowserService,
-    SpBreadcrumbService,
-} from '@streampipes/shared-ui';
-import { ActivatedRoute, Router } from '@angular/router';
-import {
-    AssetConstants,
-    GenericStorageService,
-} from '@streampipes/platform-services';
+import { Component, inject } from '@angular/core';
+import { SpAssetBrowserService } from '@streampipes/shared-ui';
+import { Router } from '@angular/router';
 import { BaseAssetDetailsDirective } from '../base-asset-details.directive';
 
 @Component({
@@ -37,23 +30,14 @@ import { BaseAssetDetailsDirective } from 
'../base-asset-details.directive';
 export class SpAssetDetailsComponent extends BaseAssetDetailsDirective {
     activeTab = 'basic';
 
-    constructor(
-        breadcrumbService: SpBreadcrumbService,
-        genericStorageService: GenericStorageService,
-        private assetBrowserService: SpAssetBrowserService,
-        route: ActivatedRoute,
-        private router: Router,
-    ) {
-        super(breadcrumbService, genericStorageService, route);
-    }
+    private router = inject(Router);
+    private assetBrowserService = inject(SpAssetBrowserService);
 
     saveAsset() {
-        this.genericStorageService
-            .updateDocument(AssetConstants.ASSET_APP_DOC_NAME, this.asset)
-            .subscribe(res => {
-                this.assetBrowserService.loadAssetData();
-                this.router.navigate(['assets']);
-            });
+        this.assetService.updateAsset(this.asset).subscribe(res => {
+            this.assetBrowserService.loadAssetData();
+            this.router.navigate(['assets']);
+        });
     }
 
     onAssetAvailable() {}
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-selection-panel/asset-selection-panel.component.ts
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-selection-panel/asset-selection-panel.component.ts
index 1e3805786b..c4eac60536 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-selection-panel/asset-selection-panel.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-selection-panel/asset-selection-panel.component.ts
@@ -109,6 +109,9 @@ export class SpAssetSelectionPanelComponent implements 
OnInit {
             assetLinks: [],
             assetType: undefined,
             assets: [],
+            assetSite: undefined,
+            labelIds: [],
+            additionalData: {},
         };
     }
 
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset.component.ts 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset.component.ts
index c6a2f42d23..bd2555907c 100644
--- 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset.component.ts
@@ -16,15 +16,12 @@
  *
  */
 
-import { Component } from '@angular/core';
+import { Component, inject } from '@angular/core';
 import { BaseAssetDetailsDirective } from '../base-asset-details.directive';
-import { SpBreadcrumbService } from '@streampipes/shared-ui';
 import {
-    GenericStorageService,
     LocationConfig,
     LocationConfigService,
 } from '@streampipes/platform-services';
-import { ActivatedRoute } from '@angular/router';
 
 @Component({
     selector: 'sp-view-asset',
@@ -35,14 +32,7 @@ import { ActivatedRoute } from '@angular/router';
 export class SpViewAssetComponent extends BaseAssetDetailsDirective {
     locationConfig: LocationConfig;
 
-    constructor(
-        breadcrumbService: SpBreadcrumbService,
-        genericStorageService: GenericStorageService,
-        route: ActivatedRoute,
-        private locationConfigService: LocationConfigService,
-    ) {
-        super(breadcrumbService, genericStorageService, route);
-    }
+    private locationConfigService = inject(LocationConfigService);
 
     onAssetAvailable() {
         this.locationConfigService
diff --git 
a/ui/src/app/assets/components/asset-overview/asset-overview.component.html 
b/ui/src/app/assets/components/asset-overview/asset-overview.component.html
index ad757b667d..e3841f1ee5 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.html
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.html
@@ -50,7 +50,7 @@
             title="Assets"
         ></sp-basic-header-title-component>
         <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
-            <div fxFlex="90" fxLayout="column">
+            <div fxFlex="100" fxLayout="column">
                 <sp-table
                     fxFlex="100"
                     [columns]="displayedColumns"
@@ -104,6 +104,16 @@
                             <mat-icon>edit</mat-icon>
                             <span>{{ 'Edit' | translate }}</span>
                         </button>
+                        <button
+                            mat-menu-item
+                            [attr.data-cy]="
+                                'manage-dashboard-permissions-' + element.name
+                            "
+                            (click)="openPermissionsDialog(element)"
+                        >
+                            <mat-icon>share</mat-icon>
+                            <span>{{ 'Manage permissions' | translate }}</span>
+                        </button>
 
                         <button
                             *ngIf="hasWritePrivilege"
diff --git 
a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts 
b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
index 38fcfd5593..14ecf88a3c 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
@@ -19,14 +19,14 @@
 import { Component, OnInit } from '@angular/core';
 import { MatTableDataSource } from '@angular/material/table';
 import {
-    AssetConstants,
-    GenericStorageService,
+    AssetManagementService,
     SpAssetModel,
 } from '@streampipes/platform-services';
 import {
     ConfirmDialogComponent,
     CurrentUserService,
     DialogService,
+    ObjectPermissionDialogComponent,
     PanelType,
     SpAssetBrowserService,
     SpBreadcrumbService,
@@ -55,7 +55,7 @@ export class SpAssetOverviewComponent implements OnInit {
     hasWritePrivilege = false;
 
     constructor(
-        private genericStorageService: GenericStorageService,
+        private assetService: AssetManagementService,
         private breadcrumbService: SpBreadcrumbService,
         private dialogService: DialogService,
         private router: Router,
@@ -76,26 +76,27 @@ export class SpAssetOverviewComponent implements OnInit {
     }
 
     loadAssets(): void {
-        this.genericStorageService
-            .getAllDocuments(AssetConstants.ASSET_APP_DOC_NAME)
-            .subscribe(result => {
-                this.existingAssets = result as SpAssetModel[];
-                this.dataSource.data = this.existingAssets;
-            });
+        this.assetService.getAllAssets().subscribe(result => {
+            this.existingAssets = result as SpAssetModel[];
+            this.dataSource.data = this.existingAssets;
+        });
     }
 
     createNewAsset() {
-        const assetModel = {
+        const assetModel: SpAssetModel = {
             assetName: 'New Asset',
             assetDescription: '',
             assetLinks: [],
             assetId: this.idGeneratorService.generate(6),
-            _id: this.idGeneratorService.generate(24),
+            elementId: this.idGeneratorService.generate(24),
             appDocType: 'asset-management',
             removable: true,
-            _rev: undefined,
+            rev: undefined,
             assets: [],
             assetType: undefined,
+            assetSite: undefined,
+            additionalData: {},
+            labelIds: [],
         };
         const dialogRef = this.dialogService.open(
             SpCreateAssetDialogComponent,
@@ -118,7 +119,7 @@ export class SpAssetOverviewComponent implements OnInit {
 
     goToDetailsView(asset: SpAssetModel, editMode = false) {
         const mode = editMode ? 'edit' : 'view';
-        this.router.navigate(['assets', 'details', asset._id, mode]);
+        this.router.navigate(['assets', 'details', asset.elementId, mode]);
     }
 
     deleteAsset(asset: SpAssetModel) {
@@ -134,17 +135,27 @@ export class SpAssetOverviewComponent implements OnInit {
         });
         dialogRef.afterClosed().subscribe(result => {
             if (result) {
-                this.genericStorageService
-                    .deleteDocument(
-                        AssetConstants.ASSET_APP_DOC_NAME,
-                        asset._id,
-                        asset._rev,
-                    )
-                    .subscribe(() => {
-                        this.loadAssets();
-                        this.assetBrowserService.loadAssetData();
-                    });
+                this.assetService.deleteAsset(asset.elementId).subscribe(() => 
{
+                    this.loadAssets();
+                    this.assetBrowserService.loadAssetData();
+                });
             }
         });
     }
+
+    openPermissionsDialog(asset: SpAssetModel) {
+        const dialogRef = this.dialogService.open(
+            ObjectPermissionDialogComponent,
+            {
+                panelType: PanelType.SLIDE_IN_PANEL,
+                title: 'Manage permissions',
+                width: '70vw',
+                data: {
+                    objectInstanceId: asset.elementId,
+                    headerTitle:
+                        'Manage permissions for asset ' + asset.assetName,
+                },
+            },
+        );
+    }
 }
diff --git 
a/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts 
b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts
index 89b5b1c36a..811665b37f 100644
--- 
a/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts
+++ 
b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts
@@ -67,7 +67,7 @@ export class ManageSiteDialogComponent implements OnInit {
             appDocType: AssetConstants.ASSET_SITES_APP_DOC_NAME,
             _id: undefined,
             label: '',
-            location: { coordinates: { latitude: 0, longitude: 0 } },
+            location: { coordinates: { latitude: 0, longitude: 0 }, zoom: 10 },
             areas: [],
         };
         this.createMode = true;
diff --git a/ui/src/app/connect/services/adapter-asset-configuration.service.ts 
b/ui/src/app/connect/services/adapter-asset-configuration.service.ts
index c7dbe9ef9a..b242382d8d 100644
--- a/ui/src/app/connect/services/adapter-asset-configuration.service.ts
+++ b/ui/src/app/connect/services/adapter-asset-configuration.service.ts
@@ -16,15 +16,15 @@
  *
  */
 
-import { Injectable, Output, EventEmitter } from '@angular/core';
+import { EventEmitter, inject, Injectable, Output } from '@angular/core';
 import {
     AssetConstants,
-    AssetManagementService,
     AssetLink,
-    LinkageData,
-    SpAssetModel,
     AssetLinkType,
+    AssetManagementService,
     GenericStorageService,
+    LinkageData,
+    SpAssetModel,
     SpAssetTreeNode,
 } from '@streampipes/platform-services';
 import { firstValueFrom } from 'rxjs';
@@ -35,10 +35,11 @@ import { firstValueFrom } from 'rxjs';
 export class AssetSaveService {
     assetLinkTypes: AssetLinkType[] = [];
     currentAsset: SpAssetModel;
-    constructor(
-        private assetService: AssetManagementService,
-        private storageService: GenericStorageService,
-    ) {
+
+    private assetService = inject(AssetManagementService);
+    private storageService = inject(GenericStorageService);
+
+    constructor() {
         this.loadAssetLinkTypes();
     }
 
@@ -119,7 +120,7 @@ export class AssetSaveService {
                         if (path.length > 2) {
                             links.forEach(linkToUpdate => {
                                 this.updateLinkLabelInDict(
-                                    current,
+                                    current as unknown as SpAssetTreeNode,
                                     path,
                                     linkToUpdate,
                                 );
@@ -225,7 +226,11 @@ export class AssetSaveService {
 
                 if (path.length > 2) {
                     links.forEach(linkToRemove => {
-                        this.deleteDictValue(current, path, linkToRemove);
+                        this.deleteDictValue(
+                            current as unknown as SpAssetTreeNode,
+                            path,
+                            linkToRemove,
+                        );
                     });
                 }
             });
diff --git a/ui/src/app/data-explorer/dialog/asset-dialog.component.ts 
b/ui/src/app/data-explorer/dialog/asset-dialog.component.ts
index 11f127203f..898da2749d 100644
--- a/ui/src/app/data-explorer/dialog/asset-dialog.component.ts
+++ b/ui/src/app/data-explorer/dialog/asset-dialog.component.ts
@@ -16,8 +16,7 @@
  *
  */
 
-import { Component, inject, Inject, Input, OnInit } from '@angular/core';
-import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { Component, inject, Input, OnInit } from '@angular/core';
 import { SpAssetTreeNode } from '@streampipes/platform-services';
 import { DialogRef } from '@streampipes/shared-ui';
 
@@ -31,6 +30,7 @@ export class AssetDialogComponent implements OnInit {
     @Input() deselectedAssets: SpAssetTreeNode[];
     @Input() originalAssets: SpAssetTreeNode[];
     @Input() dataViewId: string;
+    // TODO why is this a string and not a boolean
     @Input() editMode: string;
     @Input() cancelTitle: string;
     @Input() okTitle: string;

Reply via email to