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

markap14 pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/NIFI-15258 by this push:
     new 2b72f7a588 NIFI-15465 Allow client-specified connector component ids 
(#10768)
2b72f7a588 is described below

commit 2b72f7a5882831a72eb97aaf2791a703beb71d38
Author: Kevin Doran <[email protected]>
AuthorDate: Fri Jan 23 10:21:13 2026 -0500

    NIFI-15465 Allow client-specified connector component ids (#10768)
    
    Add verifyCreate to ConnectorDAO and test cases
---
 .../apache/nifi/web/StandardNiFiServiceFacade.java |  2 +-
 .../org/apache/nifi/web/api/ConnectorResource.java | 29 +++++++-
 .../java/org/apache/nifi/web/dao/ConnectorDAO.java |  3 +
 .../nifi/web/dao/impl/StandardConnectorDAO.java    |  9 +++
 .../apache/nifi/web/api/TestConnectorResource.java | 85 ++++++++++++++++++++++
 .../web/dao/impl/StandardConnectorDAOTest.java     | 39 ++++++++++
 6 files changed, 162 insertions(+), 5 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index bee52d0446..aa76c19a37 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -801,7 +801,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyCreateConnector(final ConnectorDTO connectorDTO) {
-        // For now, no additional verification on DTO here; creation will fail 
in DAO if invalid
+        connectorDAO.verifyCreate(connectorDTO);
     }
 
     @Override
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
index f7cd6c50f0..52ee7867f0 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
@@ -258,9 +258,6 @@ public class ConnectorResource extends ApplicationResource {
         }
 
         final ConnectorDTO requestConnector = 
requestConnectorEntity.getComponent();
-        if (requestConnector.getId() != null) {
-            throw new IllegalArgumentException("Connector ID cannot be 
specified.");
-        }
 
         if (StringUtils.isBlank(requestConnector.getType())) {
             throw new IllegalArgumentException("The type of connector to 
create must be specified.");
@@ -284,7 +281,13 @@ public class ConnectorResource extends ApplicationResource 
{
                     final ConnectorDTO connector = 
connectorEntity.getComponent();
 
                     // set the connector id as appropriate
-                    connector.setId(generateUuid());
+                    final String clientSpecifiedId = connector.getId();
+                    if (clientSpecifiedId != null) {
+                        // validate the client-specified connector id. 
uniqueness is verified by verifyCreateConnector()
+                        connector.setId(normalizeUuid(clientSpecifiedId));
+                    } else {
+                        connector.setId(generateUuid());
+                    }
 
                     // create the new connector
                     final Revision revision = getRevision(connectorEntity, 
connector.getId());
@@ -298,6 +301,24 @@ public class ConnectorResource extends ApplicationResource 
{
         );
     }
 
+    /**
+     * Validates client-specified id is a UUID and normalizes its string 
format to the lowercase digest used by NiFi.
+     *
+     * @param clientSpecifiedId the client-passed ID, which should be a valid 
UUID
+     * @return the normalized string representation of the validated UUID
+     * @throws IllegalArgumentException if the clientSpecifiedId is not a 
valid UUID
+     */
+    private String normalizeUuid(final String clientSpecifiedId) {
+        try {
+            final UUID uuid = UUID.fromString(clientSpecifiedId);
+            logger.debug("ID [{}] is a valid UUID", clientSpecifiedId);
+            return uuid.toString();
+        } catch (final Exception e) {
+            logger.error("ID [{}] is not a valid UUID", clientSpecifiedId, e);
+            throw new IllegalArgumentException("ID [" + clientSpecifiedId + "] 
is not a valid UUID.");
+        }
+    }
+
     /**
      * Retrieves the specified connector.
      *
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectorDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectorDAO.java
index d094f18ca9..6e2ff0f0c3 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectorDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectorDAO.java
@@ -23,6 +23,7 @@ import org.apache.nifi.components.ConfigVerificationResult;
 import org.apache.nifi.components.connector.ConnectorNode;
 import org.apache.nifi.components.connector.ConnectorUpdateContext;
 import org.apache.nifi.web.api.dto.ConfigurationStepConfigurationDTO;
+import org.apache.nifi.web.api.dto.ConnectorDTO;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -31,6 +32,8 @@ import java.util.Optional;
 
 public interface ConnectorDAO {
 
+    void verifyCreate(ConnectorDTO connectorDTO);
+
     boolean hasConnector(String id);
 
     ConnectorNode getConnector(String id);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectorDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectorDAO.java
index 8d18a5dff9..a2d0b17705 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectorDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectorDAO.java
@@ -36,6 +36,7 @@ import org.apache.nifi.web.NiFiCoreException;
 import org.apache.nifi.web.ResourceNotFoundException;
 import org.apache.nifi.web.api.dto.AssetReferenceDTO;
 import org.apache.nifi.web.api.dto.ConfigurationStepConfigurationDTO;
+import org.apache.nifi.web.api.dto.ConnectorDTO;
 import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
 import org.apache.nifi.web.api.dto.PropertyGroupConfigurationDTO;
 import org.apache.nifi.web.dao.ConnectorDAO;
@@ -78,6 +79,14 @@ public class StandardConnectorDAO implements ConnectorDAO {
         return flowController.getConnectorRepository().getAssetRepository();
     }
 
+    @Override
+    public void verifyCreate(final ConnectorDTO connectorDTO) {
+        final String id = connectorDTO.getId();
+        if (id != null && hasConnector(id)) {
+            throw new IllegalStateException("A Connector already exists with 
ID %s".formatted(id));
+        }
+    }
+
     @Override
     public boolean hasConnector(final String id) {
         return getConnectorRepository().getConnector(id) != null;
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
index 60702b8b23..9ec1afb42c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
@@ -544,4 +544,89 @@ public class TestConnectorResource {
         entity.setProcessGroupFlow(flowDTO);
         return entity;
     }
+
+    @Test
+    public void testCreateConnectorWithValidClientSpecifiedUuid() {
+        final String uppercaseUuid = "A1B2C3D4-E5F6-7890-ABCD-EF1234567890";
+        final String normalizedUuid = uppercaseUuid.toLowerCase();
+
+        final ConnectorEntity requestEntity = createConnectorEntity();
+        requestEntity.getComponent().setId(uppercaseUuid);
+        requestEntity.getComponent().setType(CONNECTOR_TYPE);
+        requestEntity.getRevision().setVersion(0L);
+
+        final ConnectorEntity responseEntity = createConnectorEntity();
+        responseEntity.getComponent().setId(normalizedUuid);
+
+        when(serviceFacade.createConnector(any(Revision.class), 
any(ConnectorDTO.class))).thenReturn(responseEntity);
+
+        try (Response response = 
connectorResource.createConnector(requestEntity)) {
+            assertEquals(201, response.getStatus());
+            final ConnectorEntity entity = (ConnectorEntity) 
response.getEntity();
+            assertEquals(normalizedUuid, entity.getComponent().getId());
+        }
+
+        verify(serviceFacade).verifyCreateConnector(any(ConnectorDTO.class));
+        verify(serviceFacade).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
+
+    @Test
+    public void testCreateConnectorWithInvalidClientSpecifiedUuid() {
+        final String invalidId = "not-a-valid-uuid";
+
+        final ConnectorEntity requestEntity = createConnectorEntity();
+        requestEntity.getComponent().setId(invalidId);
+        requestEntity.getComponent().setType(CONNECTOR_TYPE);
+        requestEntity.getRevision().setVersion(0L);
+
+        final IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class, () ->
+            connectorResource.createConnector(requestEntity));
+
+        assertEquals("ID [" + invalidId + "] is not a valid UUID.", 
exception.getMessage());
+
+        verify(serviceFacade, never()).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
+
+    @Test
+    public void testCreateConnectorWithNullEntity() {
+        assertThrows(IllegalArgumentException.class, () ->
+            connectorResource.createConnector(null));
+
+        verify(serviceFacade, never()).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
+
+    @Test
+    public void testCreateConnectorWithNullComponent() {
+        final ConnectorEntity requestEntity = new ConnectorEntity();
+        requestEntity.setComponent(null);
+
+        assertThrows(IllegalArgumentException.class, () ->
+            connectorResource.createConnector(requestEntity));
+
+        verify(serviceFacade, never()).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
+
+    @Test
+    public void testCreateConnectorWithInvalidRevision() {
+        final ConnectorEntity requestEntity = createConnectorEntity();
+        requestEntity.getComponent().setType(CONNECTOR_TYPE);
+        requestEntity.getRevision().setVersion(1L);
+
+        assertThrows(IllegalArgumentException.class, () ->
+            connectorResource.createConnector(requestEntity));
+
+        verify(serviceFacade, never()).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
+
+    @Test
+    public void testCreateConnectorWithBlankType() {
+        final ConnectorEntity requestEntity = createConnectorEntity();
+        requestEntity.getComponent().setType("");
+        requestEntity.getRevision().setVersion(0L);
+
+        assertThrows(IllegalArgumentException.class, () ->
+            connectorResource.createConnector(requestEntity));
+
+        verify(serviceFacade, never()).createConnector(any(Revision.class), 
any(ConnectorDTO.class));
+    }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectorDAOTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectorDAOTest.java
index 3f13d8f569..f18c011b96 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectorDAOTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectorDAOTest.java
@@ -28,6 +28,7 @@ import 
org.apache.nifi.components.connector.MutableConnectorConfigurationContext
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.web.NiFiCoreException;
 import org.apache.nifi.web.ResourceNotFoundException;
+import org.apache.nifi.web.api.dto.ConnectorDTO;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -269,5 +270,43 @@ class StandardConnectorDAOTest {
         verify(connectorRepository).removeConnector(CONNECTOR_ID);
         verify(connectorAssetRepository, never()).deleteAssets(any());
     }
+
+    @Test
+    void testVerifyCreateWithExistingConnectorId() {
+        final ConnectorDTO connectorDTO = new ConnectorDTO();
+        connectorDTO.setId(CONNECTOR_ID);
+        connectorDTO.setType("org.apache.nifi.connector.TestConnector");
+
+        
when(connectorRepository.getConnector(CONNECTOR_ID)).thenReturn(connectorNode);
+
+        final IllegalStateException exception = 
assertThrows(IllegalStateException.class, () ->
+            connectorDAO.verifyCreate(connectorDTO)
+        );
+
+        assertEquals("A Connector already exists with ID 
%s".formatted(CONNECTOR_ID), exception.getMessage());
+    }
+
+    @Test
+    void testVerifyCreateWithNewId() {
+        final ConnectorDTO connectorDTO = new ConnectorDTO();
+        connectorDTO.setId(CONNECTOR_ID);
+
+        when(connectorRepository.getConnector(CONNECTOR_ID)).thenReturn(null);
+
+        connectorDAO.verifyCreate(connectorDTO);
+
+        verify(connectorRepository).getConnector(CONNECTOR_ID);
+    }
+
+    @Test
+    void testVerifyCreateWithNullId() {
+        final ConnectorDTO connectorDTO = new ConnectorDTO();
+        connectorDTO.setId(null);
+
+        connectorDAO.verifyCreate(connectorDTO);
+
+        verify(connectorRepository, never()).getConnector(any());
+    }
+
 }
 

Reply via email to