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

commit 4c98a8842f50d118af1aa36318af406164a2c56f
Author: Mark Payne <[email protected]>
AuthorDate: Mon Jan 12 14:30:06 2026 -0500

    NIFI-15439: Ensure that Process Groups are accessible from the approp… 
(#10742)
    
    * NIFI-15439: Ensure that Process Groups are accessible from the 
appropriate endpoint but not accessible elsewhere
    
    * NIFI-15439: FlowAnalyzingRegistryClient should use an explicit Connector 
ID of null when retrieving Process Group from FlowManager
---
 .../nifi/controller/flow/AbstractFlowManager.java  | 16 +++++
 .../apache/nifi/groups/StandardProcessGroup.java   |  2 +-
 .../flow/FlowAnalyzingRegistryClientNode.java      |  3 +-
 .../flow/FlowAnalyzingRegistryClientNodeTest.java  |  2 +-
 .../apache/nifi/controller/flow/FlowManager.java   |  9 +++
 .../org/apache/nifi/controller/FlowController.java | 10 +--
 .../nifi/controller/flow/StandardFlowManager.java  |  7 +-
 .../org/apache/nifi/web/dao/impl/ComponentDAO.java |  4 +-
 .../nifi/web/dao/impl/StandardProcessGroupDAO.java |  4 +-
 .../org/apache/nifi/audit/TestLabelAuditor.java    |  2 +-
 .../apache/nifi/audit/TestProcessGroupAuditor.java |  6 +-
 .../apache/nifi/audit/TestProcessorAuditor.java    |  2 +-
 .../web/dao/impl/StandardProcessorDAOTest.java     |  2 +-
 .../web/dao/impl/TestStandardProcessGroupDAO.java  | 15 +----
 .../tests/system/NestedProcessGroupConnector.java  | 75 ++++++++++++++++++++++
 .../org.apache.nifi.components.connector.Connector |  1 +
 .../tests/system/connectors/ConnectorCrudIT.java   | 49 ++++++++++++++
 .../nifi/toolkit/client/ConnectorClient.java       |  6 +-
 .../toolkit/client/impl/JerseyConnectorClient.java | 18 +++---
 19 files changed, 185 insertions(+), 48 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
index a694c5be35..076f2efa99 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
@@ -63,6 +63,7 @@ import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
@@ -122,6 +123,21 @@ public abstract class AbstractFlowManager implements 
FlowManager {
         return allProcessGroups.get(requireNonNull(id));
     }
 
+    @Override
+    public ProcessGroup getGroup(final String groupId, final String 
connectorId) {
+        final ProcessGroup group = 
allProcessGroups.get(requireNonNull(groupId));
+        if (group == null) {
+            return null;
+        }
+
+        // If we found the group, return it only if it has the correct 
connector ID
+        if (Objects.equals(group.getConnectorIdentifier().orElse(null), 
connectorId)) {
+            return group;
+        }
+
+        return null;
+    }
+
     @Override
     public void onProcessGroupAdded(final ProcessGroup group) {
         allProcessGroups.put(group.getIdentifier(), group);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 9918eda3a1..3987ea19f9 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -2112,7 +2112,7 @@ public final class StandardProcessGroup implements 
ProcessGroup {
             return this;
         }
 
-        final ProcessGroup group = flowManager.getGroup(id);
+        final ProcessGroup group = flowManager.getGroup(id, 
getConnectorIdentifier().orElse(null));
         if (group == null) {
             return null;
         }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNode.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNode.java
index 14d555d013..faeb336efe 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNode.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNode.java
@@ -111,7 +111,8 @@ public final class FlowAnalyzingRegistryClientNode 
implements FlowRegistryClient
     }
 
     private boolean analyzeProcessGroupToRegister(final VersionedProcessGroup 
snapshot) {
-        final InstantiatedVersionedProcessGroup nonVersionedProcessGroup = 
flowMapper.mapNonVersionedProcessGroup(flowManager.getGroup(snapshot.getInstanceIdentifier()),
 serviceProvider);
+        final ProcessGroup group = 
flowManager.getGroup(snapshot.getInstanceIdentifier(), null);
+        final InstantiatedVersionedProcessGroup nonVersionedProcessGroup = 
flowMapper.mapNonVersionedProcessGroup(group, serviceProvider);
 
         flowAnalyzer.analyzeProcessGroup(nonVersionedProcessGroup);
         final List<RuleViolation> ruleViolations = 
ruleViolationsManager.getRuleViolationsForGroup(snapshot.getInstanceIdentifier()).stream()
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNodeTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNodeTest.java
index 5e3f2ce46a..e69a27a05f 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNodeTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/registry/flow/FlowAnalyzingRegistryClientNodeTest.java
@@ -90,7 +90,7 @@ class FlowAnalyzingRegistryClientNodeTest {
     @BeforeEach
     public void setUp() {
         
Mockito.when(versionedProcessGroup.getInstanceIdentifier()).thenReturn(INSTANCE_IDENTIFIER);
-        
Mockito.when(flowManager.getGroup(Mockito.anyString())).thenReturn(processGroup);
+        Mockito.when(flowManager.getGroup(Mockito.anyString(), 
Mockito.eq(null))).thenReturn(processGroup);
         
Mockito.when(flowMapper.mapNonVersionedProcessGroup(Mockito.same(processGroup), 
Mockito.same(serviceProvider))).thenReturn(nonVersionedProcessGroup);
         
Mockito.when(ruleViolation1.getEnforcementPolicy()).thenReturn(EnforcementPolicy.ENFORCE);
         
Mockito.when(ruleViolation2.getEnforcementPolicy()).thenReturn(EnforcementPolicy.ENFORCE);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
index d756e8531e..c1a363a5ab 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
@@ -165,6 +165,15 @@ public interface FlowManager extends 
ParameterProviderLookup {
      */
     ProcessGroup getGroup(String id);
 
+    /**
+     * Returns the ProcessGroup with the given ID that is managed by the 
Connector with the given ID,
+     * or null if no such ProcessGroup exists.
+     * @param groupId id of the group
+     * @param connectorId id of the connector
+     * @return the ProcessGroup with the given ID or null if none can be found
+     */
+    ProcessGroup getGroup(String groupId, String connectorId);
+
     void onProcessGroupAdded(ProcessGroup group);
 
     void onProcessGroupRemoved(ProcessGroup group);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
index 81a7115ada..1bea151791 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
@@ -52,14 +52,14 @@ import 
org.apache.nifi.components.ClassLoaderAwarePythonBridge;
 import org.apache.nifi.components.connector.ConnectorRepository;
 import 
org.apache.nifi.components.connector.ConnectorRepositoryInitializationContext;
 import org.apache.nifi.components.connector.ConnectorRequestReplicator;
+import org.apache.nifi.components.connector.ConnectorValidationTrigger;
 import 
org.apache.nifi.components.connector.StandardConnectorRepoInitializationContext;
 import org.apache.nifi.components.connector.StandardConnectorRepository;
+import org.apache.nifi.components.connector.StandardConnectorValidationTrigger;
 import 
org.apache.nifi.components.connector.secrets.ParameterProviderSecretsManager;
 import org.apache.nifi.components.connector.secrets.SecretsManager;
 import 
org.apache.nifi.components.connector.secrets.SecretsManagerInitializationContext;
 import 
org.apache.nifi.components.connector.secrets.StandardSecretsManagerInitializationContext;
-import org.apache.nifi.components.connector.ConnectorValidationTrigger;
-import org.apache.nifi.components.connector.StandardConnectorValidationTrigger;
 import org.apache.nifi.components.monitor.LongRunningTaskMonitor;
 import org.apache.nifi.components.state.StateManagerProvider;
 import org.apache.nifi.components.state.StateProvider;
@@ -229,8 +229,6 @@ import org.apache.nifi.web.revision.RevisionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.management.NotificationEmitter;
-import javax.net.ssl.SSLContext;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
@@ -263,6 +261,8 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import javax.management.NotificationEmitter;
+import javax.net.ssl.SSLContext;
 
 import static java.util.Objects.requireNonNull;
 
@@ -2180,7 +2180,7 @@ public class FlowController implements 
ReportingTaskProvider, FlowAnalysisRulePr
      * @return the process group or null if not group is found
      */
     private ProcessGroup lookupGroup(final String id) {
-        final ProcessGroup group = flowManager.getGroup(id);
+        final ProcessGroup group = flowManager.getGroup(id, null);
         if (group == null) {
             throw new IllegalStateException("No Group with ID " + id + " 
exists");
         }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java
index 96bbd8f4f7..d3874eb5cb 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java
@@ -306,10 +306,7 @@ public class StandardFlowManager extends 
AbstractFlowManager implements FlowMana
             flowController.getReloadComponent(), flowController, 
nifiProperties, statelessGroupNodeFactory,
             flowController.getAssetManager(), connectorId);
 
-        // We don't want to register the group if it's being created as part 
of a Connector
-        if (connectorId == null) {
-            onProcessGroupAdded(group);
-        }
+        onProcessGroupAdded(group);
 
         return group;
     }
@@ -750,7 +747,7 @@ public class StandardFlowManager extends 
AbstractFlowManager implements FlowMana
         final ExtensionManager extensionManager = 
flowController.getExtensionManager();
 
         final String managedGroupId = UUID.nameUUIDFromBytes((id + 
"-root").getBytes(StandardCharsets.UTF_8)).toString();
-        final ProcessGroup managedRootGroup = 
createProcessGroup(managedGroupId);
+        final ProcessGroup managedRootGroup = 
createProcessGroup(managedGroupId, id);
 
         final String paramContextId = UUID.nameUUIDFromBytes((id + 
"-parameter-context").getBytes(StandardCharsets.UTF_8)).toString();
         final String paramContextName = "Connector " + id + " Parameter 
Context";
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/ComponentDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/ComponentDAO.java
index 078644a24a..90760cbcd3 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/ComponentDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/ComponentDAO.java
@@ -63,8 +63,8 @@ public abstract class ComponentDAO {
      * @param groupId id
      * @return group
      */
-    protected ProcessGroup locateProcessGroup(FlowController flowController, 
String groupId) {
-        ProcessGroup group = flowController.getFlowManager().getGroup(groupId);
+    protected ProcessGroup locateProcessGroup(final FlowController 
flowController, final String groupId) {
+        ProcessGroup group = flowController.getFlowManager().getGroup(groupId, 
null);
 
         if (group == null) {
             throw new ResourceNotFoundException(String.format("Unable to 
locate group with id '%s'.", groupId));
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java
index f464a7375e..aa7c5bf736 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java
@@ -120,7 +120,7 @@ public class StandardProcessGroupDAO extends ComponentDAO 
implements ProcessGrou
 
     @Override
     public boolean hasProcessGroup(String groupId) {
-        return flowController.getFlowManager().getGroup(groupId) != null;
+        return flowController.getFlowManager().getGroup(groupId, null) != null;
     }
 
     @Override
@@ -411,7 +411,7 @@ public class StandardProcessGroupDAO extends ComponentDAO 
implements ProcessGrou
             .map(flowManager::getControllerServiceNode)
             .collect(Collectors.toList());
 
-        final ProcessGroup group = flowManager.getGroup(groupId);
+        final ProcessGroup group = flowManager.getGroup(groupId, null);
         if (group == null) {
             throw new IllegalArgumentException("Cannot activate Controller 
Services with IDs " + serviceIds + " because the associated Process Group (id=" 
+ groupId + ") could not be found");
         }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestLabelAuditor.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestLabelAuditor.java
index af03389ec3..48605cd64f 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestLabelAuditor.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestLabelAuditor.java
@@ -119,7 +119,7 @@ public class TestLabelAuditor {
     @Test
     void testCreateLabelAdvice() {
         final LabelDTO labelDto = getLabelDto();
-        when(flowManager.getGroup(eq(GROUP_ID))).thenReturn(processGroup);
+        when(flowManager.getGroup(eq(GROUP_ID), 
eq(null))).thenReturn(processGroup);
         when(flowManager.createLabel(eq(LABEL_ID), eq(LABEL))).thenReturn(new 
StandardLabel(LABEL_ID, LABEL));
 
         final Label label = labelDao.createLabel(GROUP_ID, labelDto);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessGroupAuditor.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessGroupAuditor.java
index 3d5f5bd1ac..b79ef9e344 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessGroupAuditor.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessGroupAuditor.java
@@ -176,7 +176,7 @@ public class TestProcessGroupAuditor {
         
when(processGroup.findRemoteGroupPort(REMOTE_GROUP_INPUT_PORT)).thenReturn(remoteGroupInputPort);
         
when(remoteGroupInputPort.getRemoteProcessGroup()).thenReturn(remoteProcessGroup);
 
-        when(flowManager.getGroup(eq(PG_1))).thenReturn(processGroup);
+        when(flowManager.getGroup(eq(PG_1), 
eq(null))).thenReturn(processGroup);
         when(flowManager.findConnectable(eq(PROC_1))).thenReturn(processor1);
         when(flowManager.findConnectable(eq(PROC_2))).thenReturn(processor2);
         
when(flowManager.findConnectable(eq(INPUT_PORT))).thenReturn(inputPort);
@@ -286,7 +286,7 @@ public class TestProcessGroupAuditor {
         when(processGroup.findInputPort(OUTPUT_PORT)).thenReturn(null);
         when(processGroup.findOutputPort(OUTPUT_PORT)).thenReturn(outputPort);
 
-        when(flowManager.getGroup(eq(PG_1))).thenReturn(processGroup);
+        when(flowManager.getGroup(eq(PG_1), 
eq(null))).thenReturn(processGroup);
         when(flowManager.findConnectable(eq(PROC_1))).thenReturn(processor1);
         when(flowManager.findConnectable(eq(PROC_2))).thenReturn(processor2);
         
when(flowManager.findConnectable(eq(INPUT_PORT))).thenReturn(inputPort);
@@ -354,7 +354,7 @@ public class TestProcessGroupAuditor {
         when(cs.getName()).thenReturn(CS_1);
         when(processGroup.findControllerService(eq(CS_1), eq(true), 
eq(true))).thenReturn(cs);
 
-        when(flowManager.getGroup(eq(PG_1))).thenReturn(processGroup);
+        when(flowManager.getGroup(eq(PG_1), 
eq(null))).thenReturn(processGroup);
         when(flowManager.getControllerServiceNode(eq(CS_1))).thenReturn(cs);
         when(flowController.getFlowManager()).thenReturn(flowManager);
         
when(flowController.getControllerServiceProvider()).thenReturn(csProvider);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessorAuditor.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessorAuditor.java
index 2b249b362b..99ebc25eae 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessorAuditor.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/audit/TestProcessorAuditor.java
@@ -147,7 +147,7 @@ class TestProcessorAuditor {
         
when(flowController.getControllerServiceProvider()).thenReturn(mock(ControllerServiceProvider.class));
         
when(flowController.getStateManagerProvider()).thenReturn(mockStateManagerProvider);
 
-        when(flowManager.getGroup(GROUP_ID)).thenReturn(processGroup);
+        when(flowManager.getGroup(GROUP_ID, null)).thenReturn(processGroup);
         when(flowManager.createProcessor(anyString(), anyString(), 
any())).thenReturn(mockProcessorNode);
 
         final Bundle bundle = getBundle();
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardProcessorDAOTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardProcessorDAOTest.java
index 30c9fdfdc9..1ae61d3dce 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardProcessorDAOTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardProcessorDAOTest.java
@@ -210,7 +210,7 @@ class StandardProcessorDAOTest {
         final Bundle bundle = new Bundle(bundleDetails, 
getClass().getClassLoader());
         final List<Bundle> bundles = List.of(bundle);
 
-        when(flowManager.getGroup(eq(groupId))).thenReturn(processGroup);
+        when(flowManager.getGroup(eq(groupId), 
eq(null))).thenReturn(processGroup);
         
when(flowController.getExtensionManager()).thenReturn(extensionManager);
         when(flowManager.createProcessor(eq(processorType), eq(id), 
eq(bundleCoordinate))).thenReturn(processorNode);
         
when(extensionManager.getBundles(eq(processorType))).thenReturn(bundles);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/TestStandardProcessGroupDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/TestStandardProcessGroupDAO.java
index 9ca3f0a837..85c55f88f9 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/TestStandardProcessGroupDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/TestStandardProcessGroupDAO.java
@@ -59,21 +59,12 @@ public class TestStandardProcessGroupDAO {
         testSubject = new StandardProcessGroupDAO();
         testSubject.setFlowController(flowController);
 
-        when(flowController
-                .getFlowManager()
-                .getGroup(PARENT_PROCESS_GROUP_ID)
-        ).thenReturn(parentProcessGroup);
-
-        when(flowController
-                .getFlowManager()
-                .getParameterContextManager()
-                .getParameterContext(PARAMETER_CONTEXT_ID)
-        ).thenReturn(parameterContext);
+        when(flowController.getFlowManager().getGroup(PARENT_PROCESS_GROUP_ID, 
null)).thenReturn(parentProcessGroup);
+        
when(flowController.getFlowManager().getParameterContextManager().getParameterContext(PARAMETER_CONTEXT_ID)).thenReturn(parameterContext);
     }
 
     @Test
     public void testCreateProcessGroup() {
-        //GIVEN
         ParameterContextReferenceEntity parameterContextReferenceEntity = new 
ParameterContextReferenceEntity();
         parameterContextReferenceEntity.setId(PARAMETER_CONTEXT_ID);
 
@@ -84,10 +75,8 @@ public class TestStandardProcessGroupDAO {
         processGroupDTO.setPosition(new PositionDTO(10.0, 20.0));
         processGroupDTO.setParameterContext(parameterContextReferenceEntity);
 
-        //WHEN
         ProcessGroup createdProcessGroup = 
testSubject.createProcessGroup(PARENT_PROCESS_GROUP_ID, processGroupDTO);
 
-        //THEN
         verify(createdProcessGroup).setParent(parentProcessGroup);
         verify(createdProcessGroup).setParameterContext(parameterContext);
         verify(createdProcessGroup).setName(PROCESS_GROUP_NAME);
diff --git 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/connectors/tests/system/NestedProcessGroupConnector.java
 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/connectors/tests/system/NestedProcessGroupConnector.java
new file mode 100644
index 0000000000..e3903a38f1
--- /dev/null
+++ 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/connectors/tests/system/NestedProcessGroupConnector.java
@@ -0,0 +1,75 @@
+/*
+ * 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.nifi.connectors.tests.system;
+
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.connector.AbstractConnector;
+import org.apache.nifi.components.connector.ConfigurationStep;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.flow.VersionedExternalFlow;
+import org.apache.nifi.flow.VersionedProcessGroup;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test Connector that creates a flow with a nested (child) process group.
+ * Used to verify that child process groups within a connector's managed flow
+ * can be properly accessed via the connector API.
+ */
+public class NestedProcessGroupConnector extends AbstractConnector {
+
+    public static final String CHILD_PROCESS_GROUP_ID = 
"child-process-group-id";
+
+    private final List<ConfigurationStep> configurationSteps = List.of();
+
+    @Override
+    protected void onStepConfigured(final String stepName, final FlowContext 
workingContext) {
+    }
+
+    @Override
+    public VersionedExternalFlow getInitialFlow() {
+        final VersionedProcessGroup childGroup = new VersionedProcessGroup();
+        childGroup.setIdentifier(CHILD_PROCESS_GROUP_ID);
+        childGroup.setName("Child Process Group");
+
+        final VersionedProcessGroup rootGroup = new VersionedProcessGroup();
+        rootGroup.setName("Nested Flow");
+        rootGroup.setProcessGroups(Set.of(childGroup));
+
+        final VersionedExternalFlow flow = new VersionedExternalFlow();
+        flow.setFlowContents(rootGroup);
+        return flow;
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verifyConfigurationStep(final String 
stepName, final Map<String, String> propertyValueOverrides, final FlowContext 
flowContext) {
+        return List.of();
+    }
+
+    @Override
+    public List<ConfigurationStep> getConfigurationSteps() {
+        return configurationSteps;
+    }
+
+    @Override
+    public void applyUpdate(final FlowContext workingFlowContext, final 
FlowContext activeFlowContext) {
+    }
+}
+
diff --git 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.components.connector.Connector
 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.components.connector.Connector
index c8e0d0ed64..a54f75d132 100644
--- 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.components.connector.Connector
+++ 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.components.connector.Connector
@@ -16,3 +16,4 @@
 org.apache.nifi.connectors.tests.system.NopConnector
 org.apache.nifi.connectors.tests.system.AssetConnector
 org.apache.nifi.connectors.tests.system.DataQueuingConnector
+org.apache.nifi.connectors.tests.system.NestedProcessGroupConnector
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorCrudIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorCrudIT.java
index fb616c6d4b..c1098c4d19 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorCrudIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorCrudIT.java
@@ -17,6 +17,7 @@
 
 package org.apache.nifi.tests.system.connectors;
 
+import jakarta.ws.rs.NotFoundException;
 import org.apache.nifi.components.ConfigVerificationResult.Outcome;
 import org.apache.nifi.components.connector.ConnectorState;
 import org.apache.nifi.tests.system.NiFiSystemIT;
@@ -24,18 +25,24 @@ import org.apache.nifi.toolkit.client.NiFiClientException;
 import org.apache.nifi.web.api.dto.ConfigVerificationResultDTO;
 import org.apache.nifi.web.api.dto.ConnectorConfigurationDTO;
 import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
+import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
 import org.apache.nifi.web.api.entity.ConnectorEntity;
 import org.apache.nifi.web.api.entity.ParameterContextsEntity;
 import org.apache.nifi.web.api.entity.ParameterProviderEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 public class ConnectorCrudIT extends NiFiSystemIT {
 
@@ -162,4 +169,46 @@ public class ConnectorCrudIT extends NiFiSystemIT {
         // Delete
         getNifiClient().getConnectorClient().deleteConnector(connector);
     }
+
+    @Test
+    public void testProcessGroupAccessibility() throws NiFiClientException, 
IOException {
+        final ConnectorEntity connector = 
getClientUtil().createConnector("NestedProcessGroupConnector");
+        assertNotNull(connector);
+
+        final ConnectorEntity updatedConnector = 
getNifiClient().getConnectorClient().getConnector(connector.getId());
+        final String managedProcessGroupId = 
updatedConnector.getComponent().getManagedProcessGroupId();
+        assertNotNull(managedProcessGroupId);
+
+        final ProcessGroupFlowEntity connectorFlowEntity = 
getNifiClient().getConnectorClient().getFlow(connector.getId(), 
managedProcessGroupId);
+        assertNotNull(connectorFlowEntity);
+
+        final ProcessGroupFlowDTO connectorFlow = 
connectorFlowEntity.getProcessGroupFlow();
+        assertNotNull(connectorFlow);
+        assertEquals(managedProcessGroupId, connectorFlow.getId());
+
+        try {
+            
getNifiClient().getFlowClient().getProcessGroup(managedProcessGroupId);
+            fail("Was able to retrieve connector-managed process group via 
FlowClient");
+        } catch (final NiFiClientException e) {
+            assertInstanceOf(NotFoundException.class, e.getCause());
+        }
+
+        final Set<ProcessGroupEntity> childGroups = 
connectorFlow.getFlow().getProcessGroups();
+        assertEquals(1, childGroups.size(), "Expected exactly one child 
process group");
+
+        final ProcessGroupEntity childGroup = childGroups.iterator().next();
+        final String childGroupId = childGroup.getId();
+        assertNotNull(childGroupId);
+
+        final ProcessGroupFlowEntity childFlowEntity = 
getNifiClient().getConnectorClient().getFlow(connector.getId(), childGroupId);
+        assertNotNull(childFlowEntity);
+        assertEquals(childGroupId, 
childFlowEntity.getProcessGroupFlow().getId());
+
+        try {
+            getNifiClient().getFlowClient().getProcessGroup(childGroupId);
+            fail("Was able to retrieve child process group of 
connector-managed flow via FlowClient");
+        } catch (final NiFiClientException e) {
+            assertInstanceOf(NotFoundException.class, e.getCause());
+        }
+    }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/ConnectorClient.java
 
b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/ConnectorClient.java
index 923604ba5f..1e95222225 100644
--- 
a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/ConnectorClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/ConnectorClient.java
@@ -234,15 +234,15 @@ public interface ConnectorClient {
     ProcessGroupFlowEntity getFlow(String connectorId) throws 
NiFiClientException, IOException;
 
     /**
-     * Gets the flow for the process group managed by a connector.
+     * Gets the flow for a specific process group within a connector's managed 
flow.
      *
      * @param connectorId the connector ID
-     * @param uiOnly whether to return only UI-specific fields
+     * @param processGroupId the process group ID within the connector's 
managed flow
      * @return the process group flow entity
      * @throws NiFiClientException if an error occurs during the request
      * @throws IOException if an I/O error occurs
      */
-    ProcessGroupFlowEntity getFlow(String connectorId, boolean uiOnly) throws 
NiFiClientException, IOException;
+    ProcessGroupFlowEntity getFlow(String connectorId, String processGroupId) 
throws NiFiClientException, IOException;
 
     /**
      * Gets the status for the process group managed by a connector.
diff --git 
a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyConnectorClient.java
 
b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyConnectorClient.java
index 8df19b09d5..b5ad5878d5 100644
--- 
a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyConnectorClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyConnectorClient.java
@@ -40,11 +40,11 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.Objects;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
+import java.util.Objects;
 
 /**
  * Jersey implementation of ConnectorClient.
@@ -361,21 +361,21 @@ public class JerseyConnectorClient extends 
AbstractJerseyClient implements Conne
 
     @Override
     public ProcessGroupFlowEntity getFlow(final String connectorId) throws 
NiFiClientException, IOException {
-        return getFlow(connectorId, false);
+        final ConnectorEntity connector = getConnector(connectorId);
+        final String managedProcessGroupId = 
connector.getComponent().getManagedProcessGroupId();
+        return getFlow(connectorId, managedProcessGroupId);
     }
 
     @Override
-    public ProcessGroupFlowEntity getFlow(final String connectorId, final 
boolean uiOnly) throws NiFiClientException, IOException {
+    public ProcessGroupFlowEntity getFlow(final String connectorId, final 
String processGroupId) throws NiFiClientException, IOException {
         Objects.requireNonNull(connectorId, "Connector ID required");
+        Objects.requireNonNull(processGroupId, "Process Group ID required");
 
         return executeAction("Error retrieving connector flow", () -> {
             WebTarget target = connectorTarget
-                    .path("/flow")
-                    .resolveTemplate("id", connectorId);
-
-            if (uiOnly) {
-                target = target.queryParam("uiOnly", "true");
-            }
+                    .path("/flow/process-groups/{processGroupId}")
+                    .resolveTemplate("id", connectorId)
+                    .resolveTemplate("processGroupId", processGroupId);
 
             return getRequestBuilder(target).get(ProcessGroupFlowEntity.class);
         });


Reply via email to