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

zehnder pushed a commit to branch 
3298-add-cypress-e2e-tests-for-compact-adapter-feature
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/3298-add-cypress-e2e-tests-for-compact-adapter-feature by this push:
     new e99eba5cc3 feat(#3298): Return Conflict Status Code for Duplicate 
Adapter ID (#3302)
e99eba5cc3 is described below

commit e99eba5cc3fa5889130b276db184bbb77741d816
Author: Philipp Zehnder <[email protected]>
AuthorDate: Wed Oct 23 16:40:58 2024 +0200

    feat(#3298): Return Conflict Status Code for Duplicate Adapter ID (#3302)
    
    * feat(#3298): Return status code conflict when adapter id is already taken
    
    * feat(#3298): Fix checkstyle
---
 .../management/AdapterMasterManagement.java        | 58 ++++++++++++++--------
 .../management/AdapterMasterManagementTest.java    | 34 ++++++-------
 .../management/AdapterResourceManager.java         | 12 +++--
 .../management/AdapterResourceManagerTest.java     | 40 +++++++++++++--
 .../rest/impl/connect/CompactAdapterResource.java  | 28 ++++++++---
 .../support/utils/connect/CompactAdapterUtils.ts   | 13 ++++-
 ui/cypress/support/utils/connect/ConnectUtils.ts   |  5 ++
 .../tests/connect/compact/addCompactAdapter.ts     | 24 +++++++++
 8 files changed, 162 insertions(+), 52 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 62c514b1e6..5c42478431 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
@@ -53,10 +53,11 @@ public class AdapterMasterManagement {
 
   private final DataStreamResourceManager dataStreamResourceManager;
 
-  public AdapterMasterManagement(IAdapterStorage adapterInstanceStorage,
-                                 AdapterResourceManager adapterResourceManager,
-                                 DataStreamResourceManager 
dataStreamResourceManager,
-                                 AdapterMetrics adapterMetrics
+  public AdapterMasterManagement(
+      IAdapterStorage adapterInstanceStorage,
+      AdapterResourceManager adapterResourceManager,
+      DataStreamResourceManager dataStreamResourceManager,
+      AdapterMetrics adapterMetrics
   ) {
     this.adapterInstanceStorage = adapterInstanceStorage;
     this.adapterMetrics = adapterMetrics;
@@ -64,28 +65,40 @@ public class AdapterMasterManagement {
     this.dataStreamResourceManager = dataStreamResourceManager;
   }
 
-  public void addAdapter(AdapterDescription ad,
-                           String adapterElementId,
-                           String principalSid)
+  public void addAdapter(
+      AdapterDescription adapterDescription,
+      String adapterId,
+      String principalSid
+  )
       throws AdapterException {
 
-    // Create elementId for adapter
+    // Create elementId for datastream
     var dataStreamElementId = 
ElementIdGenerator.makeElementId(SpDataStream.class);
-    ad.setElementId(adapterElementId);
-    ad.setCreatedAt(System.currentTimeMillis());
-    ad.setCorrespondingDataStreamElementId(dataStreamElementId);
+    adapterDescription.setElementId(adapterId);
+    adapterDescription.setCreatedAt(System.currentTimeMillis());
+    
adapterDescription.setCorrespondingDataStreamElementId(dataStreamElementId);
 
     // Add EventGrounding to AdapterDescription
     var eventGrounding = GroundingUtils.createEventGrounding();
-    ad.setEventGrounding(eventGrounding);
+    adapterDescription.setEventGrounding(eventGrounding);
 
-    this.adapterResourceManager.encryptAndCreate(ad);
+    this.adapterResourceManager.encryptAndCreate(adapterDescription);
 
-    // Create stream
-    var storedDescription = new 
SourcesManagement().createAdapterDataStream(ad, dataStreamElementId);
-    storedDescription.setCorrespondingAdapterId(adapterElementId);
+    // Stream is only created if the adpater is successfully stored
+    createDataStreamForAdapter(adapterDescription, adapterId, 
dataStreamElementId, principalSid);
+  }
+
+  private void createDataStreamForAdapter(
+      AdapterDescription adapterDescription,
+      String adapterId,
+      String streamId,
+      String principalSid
+  ) throws AdapterException {
+    var storedDescription = new SourcesManagement()
+        .createAdapterDataStream(adapterDescription, streamId);
+    storedDescription.setCorrespondingAdapterId(adapterId);
     installDataSource(storedDescription, principalSid, true);
-    LOG.info("Install source (source URL: {} in backend", ad.getElementId());
+    LOG.info("Install source (source URL: {} in backend", 
adapterDescription.getElementId());
   }
 
   public AdapterDescription getAdapter(String elementId) throws 
AdapterException {
@@ -163,7 +176,8 @@ public class AdapterMasterManagement {
       var baseUrl = new 
ExtensionsServiceEndpointGenerator().getEndpointBaseUrl(
           ad.getAppId(),
           SpServiceUrlProvider.ADAPTER,
-          ad.getDeploymentConfiguration().getDesiredServiceTags()
+          ad.getDeploymentConfiguration()
+            .getDesiredServiceTags()
       );
 
       // Update selected endpoint URL of adapter
@@ -182,9 +196,11 @@ public class AdapterMasterManagement {
     }
   }
 
-  private void installDataSource(SpDataStream stream,
-                                 String principalSid,
-                                 boolean publicElement) throws 
AdapterException {
+  private void installDataSource(
+      SpDataStream stream,
+      String principalSid,
+      boolean publicElement
+  ) throws AdapterException {
     try {
       new DataStreamVerifier(stream).verifyAndAdd(principalSid, publicElement);
     } catch (SepaParseException e) {
diff --git 
a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
 
b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
index 6d920fb751..e1e18324ce 100644
--- 
a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
+++ 
b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
@@ -36,12 +36,12 @@ import static org.mockito.Mockito.when;
 public class AdapterMasterManagementTest {
 
   @Test
-  public void getAdapterFailNull() {
-    AdapterInstanceStorageImpl adapterStorage = 
mock(AdapterInstanceStorageImpl.class);
-    AdapterResourceManager resourceManager = 
mock(AdapterResourceManager.class);
+  public void getAdapter_FailNull() {
+    var adapterStorage = mock(AdapterInstanceStorageImpl.class);
+    var resourceManager = mock(AdapterResourceManager.class);
     when(adapterStorage.findAll()).thenReturn(null);
 
-    AdapterMasterManagement adapterMasterManagement =
+    var adapterMasterManagement =
         new AdapterMasterManagement(
             adapterStorage,
             resourceManager,
@@ -53,13 +53,13 @@ public class AdapterMasterManagementTest {
   }
 
   @Test
-  public void getAdapterFail() {
-    List<AdapterDescription> adapterDescriptions = List.of(new 
AdapterDescription());
-    AdapterInstanceStorageImpl adapterStorage = 
mock(AdapterInstanceStorageImpl.class);
-    AdapterResourceManager resourceManager = 
mock(AdapterResourceManager.class);
+  public void getAdapter_Fail() {
+    var adapterDescriptions = List.of(new AdapterDescription());
+    var adapterStorage = mock(AdapterInstanceStorageImpl.class);
+    var resourceManager = mock(AdapterResourceManager.class);
     when(adapterStorage.findAll()).thenReturn(adapterDescriptions);
 
-    AdapterMasterManagement adapterMasterManagement =
+    var adapterMasterManagement =
         new AdapterMasterManagement(
             adapterStorage,
             resourceManager,
@@ -71,10 +71,10 @@ public class AdapterMasterManagementTest {
   }
 
   @Test
-  public void getAllAdaptersSuccess() throws AdapterException {
-    List<AdapterDescription> adapterDescriptions = List.of(new 
AdapterDescription());
-    AdapterInstanceStorageImpl adapterStorage = 
mock(AdapterInstanceStorageImpl.class);
-    AdapterResourceManager resourceManager = 
mock(AdapterResourceManager.class);
+  public void getAllAdapters_Success() throws AdapterException {
+    var adapterDescriptions = List.of(new AdapterDescription());
+    var adapterStorage = mock(AdapterInstanceStorageImpl.class);
+    var resourceManager = mock(AdapterResourceManager.class);
     when(adapterStorage.findAll()).thenReturn(adapterDescriptions);
 
     AdapterMasterManagement adapterMasterManagement =
@@ -91,12 +91,12 @@ public class AdapterMasterManagementTest {
   }
 
   @Test
-  public void getAllAdaptersFail() {
-    AdapterInstanceStorageImpl adapterStorage = 
mock(AdapterInstanceStorageImpl.class);
-    AdapterResourceManager resourceManager = 
mock(AdapterResourceManager.class);
+  public void getAllAdapters_Fail() {
+    var adapterStorage = mock(AdapterInstanceStorageImpl.class);
+    var resourceManager = mock(AdapterResourceManager.class);
     when(adapterStorage.findAll()).thenReturn(null);
 
-    AdapterMasterManagement adapterMasterManagement =
+    var adapterMasterManagement =
         new AdapterMasterManagement(
             adapterStorage,
             resourceManager,
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
index 72c77121aa..f477636033 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.resource.management;
 
+import org.apache.streampipes.commons.exceptions.connect.AdapterException;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.util.Cloner;
 import org.apache.streampipes.resource.management.secret.SecretProvider;
@@ -39,10 +40,15 @@ public class AdapterResourceManager extends 
AbstractResourceManager<IAdapterStor
    * @param adapterDescription input adapter description
    * @return the id of the created adapter
    */
-  public String encryptAndCreate(AdapterDescription adapterDescription) {
-    AdapterDescription encryptedAdapterDescription = 
cloneAndEncrypt(adapterDescription);
+  public String encryptAndCreate(AdapterDescription adapterDescription) throws 
AdapterException {
+    var encryptedAdapterDescription = cloneAndEncrypt(adapterDescription);
     encryptedAdapterDescription.setRev(null);
-    return db.persist(encryptedAdapterDescription).v;
+
+    try {
+      return db.persist(encryptedAdapterDescription).v;
+    } catch (org.lightcouch.DocumentConflictException e) {
+      throw new AdapterException("Conflict occurred while creating the 
adapter", e);
+    }
   }
 
   /**
diff --git 
a/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
 
b/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
index d815855490..21888492cf 100644
--- 
a/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
+++ 
b/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
@@ -18,25 +18,59 @@
 
 package org.apache.streampipes.resource.management;
 
+import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.model.Tuple2;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.storage.api.IAdapterStorage;
 
+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.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class AdapterResourceManagerTest {
 
+  private IAdapterStorage storage;
+  private AdapterResourceManager adapterResourceManager;
+
+  @BeforeEach
+  void setUp() {
+    storage = mock(IAdapterStorage.class);
+    adapterResourceManager = new AdapterResourceManager(storage);
+  }
+
   @Test
-  public void encryptAndUpdateValidateDescriptionStored() {
+  public void encryptAndUpdate_ValidateDescriptionStored() {
 
-    IAdapterStorage storage = mock(IAdapterStorage.class);
-    AdapterResourceManager adapterResourceManager = new 
AdapterResourceManager(storage);
     adapterResourceManager.encryptAndUpdate(new AdapterDescription());
 
     verify(storage, times(1)).updateElement(any());
   }
+
+  @Test
+  void encryptAndCreate_ReturnsIdOnSuccess() throws AdapterException {
+    var id = "adapterId";
+
+    when(storage.persist(any())).thenReturn(new Tuple2<>(true, id));
+    var result = adapterResourceManager.encryptAndCreate(new 
AdapterDescription());
+
+    verify(storage, times(1)).persist(any());
+    assertEquals(id, result);
+  }
+
+  @Test
+  void encryptAndCreate_ThrowsAdapterExceptionOnConflict() {
+
+    doThrow(new 
org.lightcouch.DocumentConflictException("Conflict")).when(storage).persist(any());
+
+    assertThrows(AdapterException.class, () -> 
adapterResourceManager.encryptAndCreate(new AdapterDescription()));
+  }
+
 }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
index 88424c2256..0e51f00927 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
@@ -37,6 +37,7 @@ 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;
@@ -58,7 +59,7 @@ public class CompactAdapterResource extends 
AbstractAdapterResource<AdapterMaste
   public CompactAdapterResource() {
     super(() -> new AdapterMasterManagement(
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getAdapterInstanceStorage(),
+                                  .getAdapterInstanceStorage(),
         new SpResourceManager().manageAdapters(),
         new SpResourceManager().manageDataStreams(),
         AdapterMetricsManager.INSTANCE.getAdapterMetrics()
@@ -82,11 +83,23 @@ public class CompactAdapterResource extends 
AbstractAdapterResource<AdapterMaste
     var adapterDescription = getGeneratedAdapterDescription(compactAdapter);
     var principalSid = getAuthenticatedUserSid();
 
+    var adapterId = adapterDescription.getElementId();
+
     try {
-      var adapterId = adapterDescription.getElementId();
       managementService.addAdapter(adapterDescription, adapterId, 
principalSid);
+    } catch (AdapterException e) {
+      LOG.error(
+          "Error while storing the adapterDescription with appId {}. An 
adapter with the given id already exists.",
+          adapterDescription.getAppId(), e
+      );
+      return ResponseEntity.status(HttpStatus.CONFLICT)
+                           .body(Notifications.error(e.getMessage()));
+    }
+
+    try {
       if (compactAdapter.createOptions() != null) {
-        if (compactAdapter.createOptions().persist()) {
+        if (compactAdapter.createOptions()
+                          .persist()) {
           var storedAdapter = managementService.getAdapter(adapterId);
           var status = new PersistPipelineHandler(
               getNoSqlStorage().getPipelineTemplateStorage(),
@@ -96,7 +109,8 @@ public class CompactAdapterResource extends 
AbstractAdapterResource<AdapterMaste
               getAuthenticatedUserSid()
           ).createAndStartPersistPipeline(storedAdapter);
         }
-        if (compactAdapter.createOptions().start()) {
+        if (compactAdapter.createOptions()
+                          .start()) {
           managementService.startStreamAdapter(adapterId);
         }
       }
@@ -143,8 +157,10 @@ public class CompactAdapterResource extends 
AbstractAdapterResource<AdapterMaste
     return new 
CompactAdapterManagement(generators).convertToAdapterDescription(compactAdapter);
   }
 
-  private AdapterDescription getGeneratedAdapterDescription(CompactAdapter 
compactAdapter,
-                                                            AdapterDescription 
existingAdapter) throws Exception {
+  private AdapterDescription getGeneratedAdapterDescription(
+      CompactAdapter compactAdapter,
+      AdapterDescription existingAdapter
+  ) throws Exception {
     var generators = adapterGenerationSteps.getGenerators();
     return new 
CompactAdapterManagement(generators).convertToAdapterDescription(compactAdapter,
 existingAdapter);
   }
diff --git a/ui/cypress/support/utils/connect/CompactAdapterUtils.ts 
b/ui/cypress/support/utils/connect/CompactAdapterUtils.ts
index db0c4ab444..d6d059950e 100644
--- a/ui/cypress/support/utils/connect/CompactAdapterUtils.ts
+++ b/ui/cypress/support/utils/connect/CompactAdapterUtils.ts
@@ -19,10 +19,14 @@
 import { CompactAdapter } from 
'../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 export class CompactAdapterUtils {
-    public static storeCompactAdapter(compactAdapter: CompactAdapter) {
+    public static storeCompactAdapter(
+        compactAdapter: CompactAdapter,
+        failOnStatusCode = true,
+    ) {
         return this.postCompactAdapterRequest(
             'application/json',
             compactAdapter,
+            failOnStatusCode,
         );
     }
 
@@ -30,12 +34,17 @@ export class CompactAdapterUtils {
         return this.postCompactAdapterRequest('application/yml', body);
     }
 
-    private static postCompactAdapterRequest(contentType: string, body: any) {
+    private static postCompactAdapterRequest(
+        contentType: string,
+        body: any,
+        failOnStatusCode = true,
+    ) {
         const token = window.localStorage.getItem('auth-token');
         return cy.request({
             method: 'POST',
             url: '/streampipes-backend/api/v2/connect/compact-adapters',
             body: body,
+            failOnStatusCode: failOnStatusCode,
             headers: {
                 'Accept': contentType,
                 'Content-Type': contentType,
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts 
b/ui/cypress/support/utils/connect/ConnectUtils.ts
index 660f54fa88..eec016bd61 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -417,4 +417,9 @@ export class ConnectUtils {
         ConnectBtns.startAdapter().should('have.length', 1);
         ConnectBtns.stopAdapter().should('have.length', 0);
     }
+
+    public static checkAmountOfAdapters(amount: number) {
+        ConnectUtils.goToConnect();
+        ConnectBtns.deleteAdapter().should('have.length', amount);
+    }
 }
diff --git a/ui/cypress/tests/connect/compact/addCompactAdapter.ts 
b/ui/cypress/tests/connect/compact/addCompactAdapter.ts
index 94feb782db..fe1c38f891 100644
--- a/ui/cypress/tests/connect/compact/addCompactAdapter.ts
+++ b/ui/cypress/tests/connect/compact/addCompactAdapter.ts
@@ -73,6 +73,30 @@ describe('Add Compact Adapters', () => {
         });
     });
 
+    it('Ensure correct error code when adapter with the same id already 
exists', () => {
+        const compactAdapter = getBasicCompactMachineDataSimulator()
+            .setStart()
+            .setPersist()
+            .build();
+
+        CompactAdapterUtils.storeCompactAdapter(compactAdapter).then(
+            response => {
+                expect(response.status).to.equal(200);
+                ConnectUtils.checkAmountOfAdapters(1);
+
+                // Store the same adapter a second time and validate that 
resource returns status of conflict
+                CompactAdapterUtils.storeCompactAdapter(
+                    compactAdapter,
+                    false,
+                ).then(response => {
+                    expect(response.status).to.equal(409);
+
+                    ConnectUtils.checkAmountOfAdapters(1);
+                });
+            },
+        );
+    });
+
     const getBasicCompactMachineDataSimulator = () => {
         return CompactAdapterBuilder.create(
             'org.apache.streampipes.connect.iiot.adapters.simulator.machine',

Reply via email to