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

dominikriemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 09e55cec33 fix(#5193): Missing icons and documentation during 
extension migration (#5197)
09e55cec33 is described below

commit 09e55cec33698b8f9245e2d7efe463f39468b0bc
Author: Sven Oehler <[email protected]>
AuthorDate: Tue Nov 3 16:16:28 2026 +0100

    fix(#5193): Missing icons and documentation during extension migration 
(#5197)
---
 .../management/AdapterMasterManagement.java        |   3 +-
 .../management/WorkerAdministrationManagement.java |   6 +-
 .../streampipes/manager/assets/AssetFetcher.java   |  13 +-
 .../streampipes/manager/assets/AssetManager.java   |  16 +--
 .../manager/extensions/ExtensionItemInstaller.java |   4 +-
 .../migration/AbstractMigrationManager.java        |   6 +-
 .../manager/verification/ElementVerifier.java      |  26 ++--
 .../manager/verification/TypedElementVerifier.java |  13 +-
 .../migration/MigrationAssetUpdateTest.java        | 132 +++++++++++++++++++++
 .../manager/verification/TypeExtractorTest.java    |  38 +++++-
 10 files changed, 212 insertions(+), 45 deletions(-)

diff --git 
a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
 
b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
index 74b30550ef..e433254851 100644
--- 
a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
+++ 
b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
@@ -279,6 +279,7 @@ public class AdapterMasterManagement {
         requestManager,
         resourceManager
     );
-    return verifier.verifyAndAdd(principalSid, false).isSuccess();
+    // Adapter-created streams are local descriptions without a selected 
extension service.
+    return verifier.verifyAndAdd(principalSid, false, null).isSuccess();
   }
 }
diff --git 
a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
 
b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
index 1d50d8f8f7..f5d95980ff 100644
--- 
a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
+++ 
b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
@@ -21,6 +21,7 @@ package org.apache.streampipes.connect.management.management;
 import 
org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestManager;
 import org.apache.streampipes.manager.assets.AssetManager;
+import 
org.apache.streampipes.manager.execution.endpoint.ExtensionsServiceEndpointGenerator;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTag;
 import org.apache.streampipes.resource.management.PermissionResourceManager;
@@ -36,6 +37,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 public class WorkerAdministrationManagement {
 
@@ -71,7 +73,9 @@ public class WorkerAdministrationManagement {
           if (!assetManager.existsAssetDir(adapter.getAppId())) {
             try {
               LOG.info("Updating assets for adapter {}", adapter.getAppId());
-              assetManager.storeAsset(SpServiceUrlProvider.ADAPTER, 
adapter.getAppId(), requestManager, serviceDiscovery);
+              var service = new 
ExtensionsServiceEndpointGenerator(serviceDiscovery)
+                  .selectService(adapter.getAppId(), 
SpServiceUrlProvider.ADAPTER, Set.of());
+              assetManager.storeAsset(SpServiceUrlProvider.ADAPTER, 
adapter.getAppId(), requestManager, service);
             } catch (IOException | NoServiceEndpointsAvailableException e) {
               LOG.error(
                   "Could not fetch asset for adapter {}, please try to 
manually update this adapter.",
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetFetcher.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetFetcher.java
index 08e28c0ede..51cab4e187 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetFetcher.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetFetcher.java
@@ -17,38 +17,31 @@
  */
 package org.apache.streampipes.manager.assets;
 
-import 
org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestManager;
 import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestTargets;
 import org.apache.streampipes.manager.api.extensions.ExtensionServiceRequests;
-import 
org.apache.streampipes.manager.execution.endpoint.ExtensionsServiceEndpointGenerator;
-import org.apache.streampipes.svcdiscovery.api.ISpServiceDiscovery;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.svcdiscovery.api.model.SpServiceUrlProvider;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Set;
 
 public class AssetFetcher {
 
-  private final ISpServiceDiscovery serviceDiscovery;
   private final SpServiceUrlProvider spServiceUrlProvider;
   private final String appId;
   private final ExtensionServiceRequestManager requestManager;
 
-  public AssetFetcher(ISpServiceDiscovery serviceDiscovery,
-                      SpServiceUrlProvider spServiceUrlProvider,
+  public AssetFetcher(SpServiceUrlProvider spServiceUrlProvider,
                       String appId,
                       ExtensionServiceRequestManager requestManager) {
-    this.serviceDiscovery = serviceDiscovery;
     this.spServiceUrlProvider = spServiceUrlProvider;
     this.appId = appId;
     this.requestManager = requestManager;
   }
 
-  public InputStream fetchPipelineElementAssets() throws IOException, 
NoServiceEndpointsAvailableException {
-    var service = new 
ExtensionsServiceEndpointGenerator(serviceDiscovery).selectService(appId, 
spServiceUrlProvider, Set.of());
+  public InputStream fetchPipelineElementAssets(SpServiceRegistration service) 
throws IOException {
     var requestTarget = 
ExtensionServiceRequestTargets.pipelineElementAssets(service, 
spServiceUrlProvider, appId);
     var response = 
requestManager.request(ExtensionServiceRequests.pipelineElementAssets(requestTarget));
 
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetManager.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetManager.java
index 525aa67a73..ca00c77252 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetManager.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/assets/AssetManager.java
@@ -18,10 +18,9 @@
 package org.apache.streampipes.manager.assets;
 
 import org.apache.streampipes.commons.constants.GlobalStreamPipesConstants;
-import 
org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestManager;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.storage.api.system.ISpCoreConfigurationStorage;
-import org.apache.streampipes.svcdiscovery.api.ISpServiceDiscovery;
 import org.apache.streampipes.svcdiscovery.api.model.SpServiceUrlProvider;
 
 import org.apache.commons.io.FileUtils;
@@ -59,12 +58,13 @@ public class AssetManager {
   }
 
   public void storeAsset(SpServiceUrlProvider spServiceUrlProvider,
-                                String appId,
-                                ExtensionServiceRequestManager requestManager,
-                                ISpServiceDiscovery serviceDiscovery) throws 
IOException, NoServiceEndpointsAvailableException {
-    InputStream assetStream = new AssetFetcher(serviceDiscovery, 
spServiceUrlProvider, appId, requestManager)
-        .fetchPipelineElementAssets();
-    new AssetExtractor(assetStream, appId, 
coreConfigurationStorage).extractAssetContents();
+                         String appId,
+                         ExtensionServiceRequestManager requestManager,
+                         SpServiceRegistration service) throws IOException {
+    var fetcher = new AssetFetcher(spServiceUrlProvider, appId, 
requestManager);
+    try (InputStream assetStream = 
fetcher.fetchPipelineElementAssets(service)) {
+      new AssetExtractor(assetStream, appId, 
coreConfigurationStorage).extractAssetContents();
+    }
   }
 
   public void deleteAsset(String appId) throws IOException {
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/extensions/ExtensionItemInstaller.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/extensions/ExtensionItemInstaller.java
index 4671a8cae7..a9b29dfb70 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/extensions/ExtensionItemInstaller.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/extensions/ExtensionItemInstaller.java
@@ -54,14 +54,14 @@ public class ExtensionItemInstaller {
     var requestTarget = getDescriptionRequestTarget(req);
     var description = fetchDescription(requestTarget);
     return new TypeExtractor(description, descriptionStorage, requestManager,
-        resourceManager).getTypeVerifier().verifyAndAdd(principalSid, 
req.publicElement());
+        resourceManager).getTypeVerifier().verifyAndAdd(principalSid, 
req.publicElement(), service);
   }
 
   public Message updateExtension(ExtensionItemInstallationRequest req) throws 
IOException, SepaParseException {
     var requestTarget = getDescriptionRequestTarget(req);
     var description = fetchDescription(requestTarget);
     return new TypeExtractor(description, descriptionStorage, requestManager,
-        resourceManager).getTypeVerifier().verifyAndUpdate();
+        resourceManager).getTypeVerifier().verifyAndUpdate(service);
   }
 
   private ExtensionServiceRequestTarget 
getDescriptionRequestTarget(ExtensionItemInstallationRequest req) {
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/migration/AbstractMigrationManager.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/migration/AbstractMigrationManager.java
index c933c3aefd..5afbdbaf4f 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/migration/AbstractMigrationManager.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/migration/AbstractMigrationManager.java
@@ -148,21 +148,21 @@ public abstract class AbstractMigrationManager {
         .forEach(config -> {
           if (isInstalled(config.modelType(), config.targetAppId())) {
             var requestTarget = getRequestTarget(config.modelType(), 
config.targetAppId(), service);
-            performUpdate(requestTarget);
+            performUpdate(requestTarget, service);
           }
         });
   }
 
   protected abstract boolean isInstalled(SpServiceTagPrefix modelType, String 
appId);
 
-  protected void performUpdate(ExtensionServiceRequestTarget requestTarget) {
+  protected void performUpdate(ExtensionServiceRequestTarget requestTarget, 
SpServiceRegistration service) {
 
     try {
       var entityPayload = requestManager
           .request(ExtensionServiceRequests.descriptionUpdate(requestTarget, 
resourceManager))
           .responseBody();
       var updateResult = new TypeExtractor(entityPayload, descriptionStorage, 
requestManager, resourceManager)
-          .getTypeVerifier().verifyAndUpdate();
+          .getTypeVerifier().verifyAndUpdate(service);
       if (!updateResult.isSuccess()) {
         LOG.error(
             "Updating the pipeline element description failed: {}",
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/ElementVerifier.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/ElementVerifier.java
index b18fdb3c84..50db874da2 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/ElementVerifier.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/ElementVerifier.java
@@ -23,6 +23,7 @@ import org.apache.streampipes.manager.assets.AssetManager;
 import org.apache.streampipes.model.base.NamedStreamPipesEntity;
 import org.apache.streampipes.model.client.user.Permission;
 import org.apache.streampipes.model.client.user.PermissionBuilder;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.model.message.ErrorMessage;
 import org.apache.streampipes.model.message.Message;
 import org.apache.streampipes.model.message.Notification;
@@ -86,7 +87,7 @@ public abstract class ElementVerifier<T extends 
NamedStreamPipesEntity> {
 
   protected abstract void update();
 
-  public Message verifyAndAdd(String principalSid, boolean publicElement) {
+  public Message verifyAndAdd(String principalSid, boolean publicElement, 
SpServiceRegistration service) {
     var transformError = transformEntity();
     if (transformError != null) {
       return transformError;
@@ -96,9 +97,10 @@ public abstract class ElementVerifier<T extends 
NamedStreamPipesEntity> {
     if (state == StorageState.STORED) {
       createAndStorePermission(principalSid, publicElement);
       try {
-        storeAssets();
+        storeAssets(service);
       } catch (IOException | NoServiceEndpointsAvailableException e) {
         LOG.error("Could not store assets for app id '{}'", 
elementDescription.getAppId(), e);
+        return assetErrorMessage();
       }
       return successMessage();
     } else {
@@ -107,7 +109,7 @@ public abstract class ElementVerifier<T extends 
NamedStreamPipesEntity> {
 
   }
 
-  public Message verifyAndUpdate() {
+  public Message verifyAndUpdate(SpServiceRegistration service) {
     var transformError = transformEntity();
     if (transformError != null) {
       return transformError;
@@ -115,23 +117,31 @@ public abstract class ElementVerifier<T extends 
NamedStreamPipesEntity> {
 
     update();
     try {
-      updateAssets();
+      updateAssets(service);
     } catch (IOException | NoServiceEndpointsAvailableException e) {
       LOG.error("Could not update assets for app id '{}'", 
elementDescription.getAppId(), e);
+      return assetErrorMessage();
     }
     return successMessage();
 
   }
 
-  protected abstract void storeAssets() throws IOException, 
NoServiceEndpointsAvailableException;
+  protected abstract void storeAssets(SpServiceRegistration service)
+      throws IOException, NoServiceEndpointsAvailableException;
 
-  protected void updateAssets() throws IOException, 
NoServiceEndpointsAvailableException {
+  protected void updateAssets(SpServiceRegistration service) throws 
IOException, NoServiceEndpointsAvailableException {
     if (elementDescription.isIncludesAssets()) {
-      assetManager.deleteAsset(elementDescription.getAppId());
-      storeAssets();
+      storeAssets(service);
     }
   }
 
+  private Message assetErrorMessage() {
+    return new ErrorMessage(elementDescription.getName(), List.of(
+        NotificationType.STORAGE_SUCCESS.uiNotification(),
+        new Notification("Asset update failed", "The element description was 
stored, but its assets could not be updated. "
+            + "Please retry the update.")));
+  }
+
   private Message successMessage() {
     List<Notification> notifications = new ArrayList<>();
     notifications.add(NotificationType.STORAGE_SUCCESS.uiNotification());
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/TypedElementVerifier.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/TypedElementVerifier.java
index b8bb1ac341..5f1a12dae7 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/TypedElementVerifier.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/verification/TypedElementVerifier.java
@@ -18,12 +18,11 @@
 
 package org.apache.streampipes.manager.verification;
 
-import 
org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestManager;
 import org.apache.streampipes.model.base.NamedStreamPipesEntity;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.resource.management.SpResourceManager;
 import 
org.apache.streampipes.storage.api.pipeline.IPipelineElementDescriptionStorage;
-import org.apache.streampipes.svcdiscovery.api.ISpServiceDiscovery;
 import org.apache.streampipes.svcdiscovery.api.model.SpServiceUrlProvider;
 
 import java.io.IOException;
@@ -37,7 +36,6 @@ public class TypedElementVerifier<T extends 
NamedStreamPipesEntity> extends Elem
   private final Consumer<T> updateOperation;
   private final SpServiceUrlProvider serviceUrlProvider;
   private final ExtensionServiceRequestManager requestManager;
-  private final ISpServiceDiscovery serviceDiscovery;
 
   public TypedElementVerifier(
       String graphData,
@@ -56,7 +54,6 @@ public class TypedElementVerifier<T extends 
NamedStreamPipesEntity> extends Elem
     this.updateOperation = updateOperation;
     this.serviceUrlProvider = serviceUrlProvider;
     this.requestManager = requestManager;
-    this.serviceDiscovery = resourceManager.getServiceDiscovery();
   }
 
   public TypedElementVerifier(
@@ -75,7 +72,6 @@ public class TypedElementVerifier<T extends 
NamedStreamPipesEntity> extends Elem
     this.updateOperation = updateOperation;
     this.serviceUrlProvider = serviceUrlProvider;
     this.requestManager = requestManager;
-    this.serviceDiscovery = resourceManager.getServiceDiscovery();
   }
 
   @Override
@@ -93,9 +89,12 @@ public class TypedElementVerifier<T extends 
NamedStreamPipesEntity> extends Elem
   }
 
   @Override
-  protected void storeAssets() throws IOException, 
NoServiceEndpointsAvailableException {
+  protected void storeAssets(SpServiceRegistration service) throws IOException 
{
     if (elementDescription.isIncludesAssets()) {
-      assetManager.storeAsset(serviceUrlProvider, 
elementDescription.getAppId(), requestManager, serviceDiscovery);
+      if (service == null) {
+        throw new IOException("No source service provided for assets of " + 
elementDescription.getAppId());
+      }
+      assetManager.storeAsset(serviceUrlProvider, 
elementDescription.getAppId(), requestManager, service);
     }
   }
 }
diff --git 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/migration/MigrationAssetUpdateTest.java
 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/migration/MigrationAssetUpdateTest.java
new file mode 100644
index 0000000000..fe7fb4ca13
--- /dev/null
+++ 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/migration/MigrationAssetUpdateTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.migration;
+
+import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceOperationResult;
+import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestManager;
+import org.apache.streampipes.manager.util.AuthTokenProvider;
+import org.apache.streampipes.model.client.user.Principal;
+import org.apache.streampipes.model.configuration.SpCoreConfiguration;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
+import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTagPrefix;
+import 
org.apache.streampipes.model.extensions.transport.ExtensionServiceBrokerOperations;
+import org.apache.streampipes.model.graph.DataSinkDescription;
+import org.apache.streampipes.model.migration.ModelMigratorConfig;
+import org.apache.streampipes.resource.management.SpResourceManager;
+import org.apache.streampipes.resource.management.UserResourceManager;
+import 
org.apache.streampipes.storage.api.pipeline.IPipelineElementDescriptionStorage;
+import org.apache.streampipes.storage.api.system.ISpCoreConfigurationStorage;
+import org.apache.streampipes.svcdiscovery.api.ISpServiceDiscovery;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+class MigrationAssetUpdateTest {
+
+  private static final String APP_ID = 
"org.apache.streampipes.sinks.internal.jvm.dataset";
+
+  @TempDir
+  Path directory;
+
+  private SpResourceManager resources;
+  private IPipelineElementDescriptionStorage descriptions;
+  private ISpServiceDiscovery discovery;
+  private SpServiceRegistration service;
+
+  @BeforeEach
+  void setUp() {
+    resources = mock(SpResourceManager.class);
+    descriptions = mock(IPipelineElementDescriptionStorage.class);
+    discovery = mock(ISpServiceDiscovery.class);
+    when(resources.getServiceDiscovery()).thenReturn(discovery);
+    var configurationStorage = mock(ISpCoreConfigurationStorage.class);
+    var configuration = new SpCoreConfiguration();
+    configuration.setAssetDir(directory.toString());
+    when(configurationStorage.get()).thenReturn(configuration);
+    
when(resources.getCoreConfigurationStorage()).thenReturn(configurationStorage);
+    service = new SpServiceRegistration();
+    service.setSvcId("streampipes-current-instance");
+    service.setHost("localhost");
+    service.setPort(8090);
+  }
+
+  @Test
+  void migrationFetchesAssetsFromTheServiceProvidingTheDescription() throws 
Exception {
+    var archive = assetArchive();
+    ExtensionServiceRequestManager requests = request -> {
+      assertEquals(service.getSvcId(), request.target().serviceId());
+      if 
(request.target().operation().equals(ExtensionServiceBrokerOperations.PIPELINE_ELEMENT_ASSETS.operationId()))
 {
+        return new ExtensionServiceOperationResult(200, archive);
+      }
+      return new ExtensionServiceOperationResult(200, 
description().getBytes(StandardCharsets.UTF_8));
+    };
+    var manager = new AbstractMigrationManager(requests, resources, 
descriptions) {
+      @Override
+      protected boolean isInstalled(SpServiceTagPrefix modelType, String 
appId) {
+        return true;
+      }
+    };
+
+    var users = mock(UserResourceManager.class);
+    when(resources.manageUsers()).thenReturn(users);
+    when(users.getServiceAdmin()).thenReturn(mock(Principal.class));
+    try (var tokens = mockConstruction(AuthTokenProvider.class)) {
+      manager.updateDescriptions(List.of(new ModelMigratorConfig(APP_ID, 
SpServiceTagPrefix.DATA_SINK, 1, 2)), service);
+    }
+
+    assertEquals("new icon", 
Files.readString(directory.resolve(APP_ID).resolve("icon.png")));
+    verify(descriptions).update(any(DataSinkDescription.class));
+    verifyNoInteractions(discovery);
+  }
+
+  private String description() {
+    return "{\"@class\":\"" + DataSinkDescription.class.getCanonicalName()
+        + "\",\"appId\":\"" + APP_ID + 
"\",\"elementId\":\"test-element\",\"includesAssets\":true}";
+  }
+
+  private byte[] assetArchive() throws IOException {
+    var bytes = new ByteArrayOutputStream();
+    try (var zip = new ZipOutputStream(bytes)) {
+      zip.putNextEntry(new ZipEntry("icon.png"));
+      zip.write("new icon".getBytes(StandardCharsets.UTF_8));
+      zip.closeEntry();
+      zip.putNextEntry(new ZipEntry("documentation.md"));
+      zip.write("documentation".getBytes(StandardCharsets.UTF_8));
+      zip.closeEntry();
+    }
+    return bytes.toByteArray();
+  }
+}
diff --git 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/verification/TypeExtractorTest.java
 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/verification/TypeExtractorTest.java
index 86d345ce87..4b13bc0774 100644
--- 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/verification/TypeExtractorTest.java
+++ 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/verification/TypeExtractorTest.java
@@ -23,6 +23,7 @@ import 
org.apache.streampipes.manager.api.extensions.ExtensionServiceRequestMana
 import org.apache.streampipes.manager.verification.extractor.TypeExtractor;
 import org.apache.streampipes.model.SpDataStream;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.model.graph.DataProcessorDescription;
 import org.apache.streampipes.model.graph.DataSinkDescription;
 import org.apache.streampipes.model.message.NotificationType;
@@ -35,15 +36,20 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 public class TypeExtractorTest {
 
+  private final SpServiceRegistration service = new SpServiceRegistration();
+
   private IPipelineElementDescriptionStorage storageApi;
   private SpResourceManager resourceManager;
   private final ExtensionServiceRequestManager requestManager = 
mock(ExtensionServiceRequestManager.class);
@@ -60,7 +66,7 @@ public class TypeExtractorTest {
   public void verifyAndUpdateDataStream() throws SepaParseException {
     var message = new TypeExtractor(payload(SpDataStream.class), storageApi, 
requestManager, resourceManager)
         .getTypeVerifier()
-        .verifyAndUpdate();
+        .verifyAndUpdate(service);
 
     assertTrue(message.isSuccess());
     verify(storageApi).update(any(SpDataStream.class));
@@ -70,7 +76,7 @@ public class TypeExtractorTest {
   public void verifyAndUpdateDataProcessor() throws SepaParseException {
     var message = new TypeExtractor(payload(DataProcessorDescription.class), 
storageApi, requestManager, resourceManager)
         .getTypeVerifier()
-        .verifyAndUpdate();
+        .verifyAndUpdate(service);
 
     assertTrue(message.isSuccess());
     verify(storageApi).update(any(DataProcessorDescription.class));
@@ -80,7 +86,7 @@ public class TypeExtractorTest {
   public void verifyAndUpdateDataSink() throws SepaParseException {
     var message = new TypeExtractor(payload(DataSinkDescription.class), 
storageApi, requestManager, resourceManager)
         .getTypeVerifier()
-        .verifyAndUpdate();
+        .verifyAndUpdate(service);
 
     assertTrue(message.isSuccess());
     verify(storageApi).update(any(DataSinkDescription.class));
@@ -90,7 +96,7 @@ public class TypeExtractorTest {
   public void verifyAndUpdateAdapter() throws SepaParseException {
     var message = new TypeExtractor(payload(AdapterDescription.class), 
storageApi, requestManager, resourceManager)
         .getTypeVerifier()
-        .verifyAndUpdate();
+        .verifyAndUpdate(service);
 
     assertTrue(message.isSuccess());
     verify(storageApi).update(any(AdapterDescription.class));
@@ -100,12 +106,34 @@ public class TypeExtractorTest {
   public void 
verifyAndUpdateWithoutNameOrIcon_onlyContainsStorageSuccessNotification() 
throws SepaParseException {
     var message = new TypeExtractor(payload(DataProcessorDescription.class), 
storageApi, requestManager, resourceManager)
         .getTypeVerifier()
-        .verifyAndUpdate();
+        .verifyAndUpdate(service);
 
     assertEquals(1, message.getNotifications().size());
     assertEquals(NotificationType.STORAGE_SUCCESS.title(), 
message.getNotifications().get(0).getTitle());
   }
 
+  @Test
+  public void updateWithoutAssetsDoesNotRequireAService() throws 
SepaParseException {
+    var message = new TypeExtractor(payload(SpDataStream.class), storageApi, 
requestManager, resourceManager)
+        .getTypeVerifier().verifyAndUpdate(null);
+
+    assertTrue(message.isSuccess());
+    verifyNoInteractions(requestManager);
+    verify(resourceManager, never()).getServiceDiscovery();
+  }
+
+  @Test
+  public void updateWithAssetsReportsMissingServiceWithoutDiscovery() throws 
SepaParseException {
+    var description = 
payload(DataSinkDescription.class).replace("\"elementId\"", 
"\"includesAssets\":true,\"elementId\"");
+    var message = new TypeExtractor(description, storageApi, requestManager, 
resourceManager)
+        .getTypeVerifier().verifyAndUpdate(null);
+
+    assertFalse(message.isSuccess());
+    assertTrue(message.getNotifications().stream().anyMatch(n -> 
n.getTitle().equals("Asset update failed")));
+    verifyNoInteractions(requestManager);
+    verify(resourceManager, never()).getServiceDiscovery();
+  }
+
   @Test
   public void missingClassProperty_throwsSepaParseException() {
     assertThrows(

Reply via email to