This is an automated email from the ASF dual-hosted git repository.
rfellows 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 8b56c99281 NIFI-15453: Adding an endpoint to return all controller
services for … (#10770)
8b56c99281 is described below
commit 8b56c99281fc7ff4c245d4c8f1c03f1b4baae809
Author: Matt Gilman <[email protected]>
AuthorDate: Thu Jan 15 14:19:10 2026 -0500
NIFI-15453: Adding an endpoint to return all controller services for …
(#10770)
* NIFI-15453: Adding an endpoint to return all controller services for a
process group in a connector.
* NIFI-15453: Addressing review feedback.
This closes #10770
---
.../org/apache/nifi/web/NiFiServiceFacade.java | 3 +
.../apache/nifi/web/StandardNiFiServiceFacade.java | 22 ++++++
.../org/apache/nifi/web/api/ConnectorResource.java | 79 ++++++++++++++++++++++
.../apache/nifi/web/api/TestConnectorResource.java | 34 ++++++++++
4 files changed, 138 insertions(+)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 09761a5076..fba789287f 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -224,6 +224,9 @@ public interface NiFiServiceFacade {
ProcessGroupStatusEntity getConnectorProcessGroupStatus(String id, Boolean
recursive);
+ Set<ControllerServiceEntity> getConnectorControllerServices(String
connectorId, String processGroupId, boolean includeAncestorGroups,
+ boolean
includeDescendantGroups, boolean includeReferencingComponents);
+
void verifyCanVerifyConnectorConfigurationStep(String connectorId, String
configurationStepName);
List<ConfigVerificationResultDTO>
performConnectorConfigurationStepVerification(String connectorId, String
configurationStepName, ConfigurationStepConfigurationDTO
configurationStepConfiguration);
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 84087cbe70..323630ad03 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
@@ -3720,6 +3720,28 @@ public class StandardNiFiServiceFacade implements
NiFiServiceFacade {
return entityFactory.createProcessGroupStatusEntity(dto, permissions);
}
+ @Override
+ public Set<ControllerServiceEntity> getConnectorControllerServices(final
String connectorId, final String processGroupId,
+ final boolean includeAncestorGroups, final boolean
includeDescendantGroups, final boolean includeReferencingComponents) {
+ final ConnectorNode connectorNode =
connectorDAO.getConnector(connectorId);
+ final ProcessGroup managedProcessGroup =
connectorNode.getActiveFlowContext().getManagedProcessGroup();
+ final ProcessGroup targetProcessGroup =
managedProcessGroup.findProcessGroup(processGroupId);
+ if (targetProcessGroup == null) {
+ throw new ResourceNotFoundException("Process Group with ID " +
processGroupId + " was not found within Connector " + connectorId);
+ }
+
+ final Set<ControllerServiceNode> serviceNodes = new HashSet<>();
+
serviceNodes.addAll(targetProcessGroup.getControllerServices(includeAncestorGroups));
+
+ if (includeDescendantGroups) {
+
serviceNodes.addAll(targetProcessGroup.findAllControllerServices());
+ }
+
+ return serviceNodes.stream()
+ .map(serviceNode -> createControllerServiceEntity(serviceNode,
includeReferencingComponents))
+ .collect(Collectors.toSet());
+ }
+
@Override
public void verifyCanVerifyConnectorConfigurationStep(final String
connectorId, final String configurationStepName) {
connectorDAO.verifyCanVerifyConfigurationStep(connectorId,
configurationStepName);
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 c2f7b4cddf..cb5146afdb 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
@@ -86,6 +86,8 @@ import
org.apache.nifi.web.api.entity.ConfigurationStepNamesEntity;
import org.apache.nifi.web.api.entity.ConnectorEntity;
import org.apache.nifi.web.api.entity.ConnectorPropertyAllowableValuesEntity;
import org.apache.nifi.web.api.entity.ConnectorRunStatusEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
import org.apache.nifi.web.api.entity.SearchResultsEntity;
@@ -104,6 +106,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -128,6 +131,7 @@ public class ConnectorResource extends ApplicationResource {
private NiFiServiceFacade serviceFacade;
private Authorizer authorizer;
private FlowResource flowResource;
+ private ControllerServiceResource controllerServiceResource;
private UploadRequestReplicator uploadRequestReplicator;
private final RequestManager<VerifyConnectorConfigStepRequestEntity,
List<ConfigVerificationResultDTO>> configVerificationRequestManager =
@@ -1411,6 +1415,76 @@ public class ConnectorResource extends
ApplicationResource {
return generateOkResponse(entity).build();
}
+ /**
+ * Retrieves all the controller services in the specified process group
within a connector.
+ *
+ * @param connectorId The id of the connector
+ * @param processGroupId The process group id within the connector's
hierarchy
+ * @param includeAncestorGroups Whether to include ancestor process groups
+ * @param includeDescendantGroups Whether to include descendant process
groups
+ * @param includeReferences Whether to include services' referencing
components in the response
+ * @return A controllerServicesEntity.
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces(MediaType.APPLICATION_JSON)
+
@Path("/{connectorId}/flow/process-groups/{processGroupId}/controller-services")
+ @Operation(
+ summary = "Gets all controller services for a process group within
a connector",
+ responses = {
+ @ApiResponse(responseCode = "200", content =
@Content(schema = @Schema(implementation = ControllerServicesEntity.class))),
+ @ApiResponse(responseCode = "400", description = "NiFi was
unable to complete the request because it was invalid. The request should not
be retried without modification."),
+ @ApiResponse(responseCode = "401", description = "Client
could not be authenticated."),
+ @ApiResponse(responseCode = "403", description = "Client
is not authorized to make this request."),
+ @ApiResponse(responseCode = "404", description = "The
specified resource could not be found."),
+ @ApiResponse(responseCode = "409", description = "The
request was valid but NiFi was not in the appropriate state to process it.")
+ },
+ security = {
+ @SecurityRequirement(name = "Read - /connectors/{uuid}")
+ },
+ description = "Returns the controller services for the specified
process group within the connector's hierarchy. The processGroupId can be " +
+ "obtained from the managedProcessGroupId field of the
ConnectorDTO for the root process group, or from child process " +
+ "groups within the flow."
+ )
+ public Response getControllerServicesFromConnectorProcessGroup(
+ @Parameter(description = "The connector id.", required = true)
+ @PathParam("connectorId") final String connectorId,
+ @Parameter(description = "The process group id.", required = true)
+ @PathParam("processGroupId") final String processGroupId,
+ @Parameter(description = "Whether or not to include
parent/ancestor process groups")
+ @QueryParam("includeAncestorGroups")
+ @DefaultValue("true") final boolean includeAncestorGroups,
+ @Parameter(description = "Whether or not to include descendant
process groups")
+ @QueryParam("includeDescendantGroups")
+ @DefaultValue("false") final boolean includeDescendantGroups,
+ @Parameter(description = "Whether or not to include services'
referencing components in the response")
+ @QueryParam("includeReferencingComponents")
+ @DefaultValue("true") final boolean includeReferences) {
+
+ if (isReplicateRequest()) {
+ return replicate(HttpMethod.GET);
+ }
+
+ // authorize access to the connector
+ serviceFacade.authorizeAccess(lookup -> {
+ final Authorizable connector = lookup.getConnector(connectorId);
+ connector.authorize(authorizer, RequestAction.READ,
NiFiUserUtils.getNiFiUser());
+ });
+
+ // get the controller services for the specified process group within
the connector's hierarchy
+ final Set<ControllerServiceEntity> controllerServices =
serviceFacade.getConnectorControllerServices(
+ connectorId, processGroupId, includeAncestorGroups,
includeDescendantGroups, includeReferences);
+
controllerServiceResource.populateRemainingControllerServiceEntitiesContent(controllerServices);
+
+ // create the response entity
+ final ControllerServicesEntity entity = new ControllerServicesEntity();
+ entity.setCurrentTime(new Date());
+ entity.setControllerServices(controllerServices);
+
+ // generate the response
+ return generateOkResponse(entity).build();
+ }
+
/**
* Retrieves the status for the process group managed by the specified
connector.
*
@@ -1710,6 +1784,11 @@ public class ConnectorResource extends
ApplicationResource {
this.flowResource = flowResource;
}
+ @Autowired
+ public void setControllerServiceResource(final ControllerServiceResource
controllerServiceResource) {
+ this.controllerServiceResource = controllerServiceResource;
+ }
+
@Autowired(required = false)
public void setUploadRequestReplicator(final UploadRequestReplicator
uploadRequestReplicator) {
this.uploadRequestReplicator = uploadRequestReplicator;
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 5c22f82fd1..c218d87d68 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
@@ -36,6 +36,8 @@ import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
import org.apache.nifi.web.api.entity.ConnectorEntity;
import org.apache.nifi.web.api.entity.ConnectorPropertyAllowableValuesEntity;
import org.apache.nifi.web.api.entity.ConnectorRunStatusEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
import org.apache.nifi.web.api.entity.SecretsEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
@@ -49,6 +51,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.net.URI;
import java.util.List;
+import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -91,6 +94,9 @@ public class TestConnectorResource {
@Mock
private FlowResource flowResource;
+ @Mock
+ private ControllerServiceResource controllerServiceResource;
+
private static final String CONNECTOR_ID = "test-connector-id";
private static final String CONNECTOR_NAME = "Test Connector";
private static final String CONNECTOR_TYPE = "TestConnectorType";
@@ -116,6 +122,7 @@ public class TestConnectorResource {
connectorResource.setServiceFacade(serviceFacade);
connectorResource.setFlowResource(flowResource);
+
connectorResource.setControllerServiceResource(controllerServiceResource);
connectorResource.httpServletRequest = httpServletRequest;
connectorResource.properties = properties;
connectorResource.uriInfo = uriInfo;
@@ -384,6 +391,33 @@ public class TestConnectorResource {
verify(serviceFacade, never()).getConnectorFlow(anyString(),
anyString(), eq(false));
}
+ @Test
+ public void testGetControllerServicesFromConnectorProcessGroup() {
+ final Set<ControllerServiceEntity> controllerServices = Set.of();
+ when(serviceFacade.getConnectorControllerServices(CONNECTOR_ID,
PROCESS_GROUP_ID, true, false, true)).thenReturn(controllerServices);
+
+ try (Response response =
connectorResource.getControllerServicesFromConnectorProcessGroup(CONNECTOR_ID,
PROCESS_GROUP_ID, true, false, true)) {
+ assertEquals(200, response.getStatus());
+ final ControllerServicesEntity entity = (ControllerServicesEntity)
response.getEntity();
+ assertEquals(controllerServices, entity.getControllerServices());
+ }
+
+ verify(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+ verify(serviceFacade).getConnectorControllerServices(CONNECTOR_ID,
PROCESS_GROUP_ID, true, false, true);
+
verify(controllerServiceResource).populateRemainingControllerServiceEntitiesContent(controllerServices);
+ }
+
+ @Test
+ public void
testGetControllerServicesFromConnectorProcessGroupNotAuthorized() {
+
doThrow(AccessDeniedException.class).when(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+
+ assertThrows(AccessDeniedException.class, () ->
+
connectorResource.getControllerServicesFromConnectorProcessGroup(CONNECTOR_ID,
PROCESS_GROUP_ID, true, false, true));
+
+ verify(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+ verify(serviceFacade,
never()).getConnectorControllerServices(anyString(), anyString(), eq(true),
eq(false), eq(true));
+ }
+
private ConnectorEntity createConnectorEntity() {
final ConnectorEntity entity = new ConnectorEntity();