From aeaf953e06615bd2c05b6ced4b8d475af09b00af Mon Sep 17 00:00:00 2001 From: Michael Hogue Date: Mon, 2 Mar 2020 16:06:28 -0500 Subject: [PATCH] NIFI-5925: Added controller services to set of components that are searched NIFI-5925: cleanup, add negative test NIFI-5925: fixed checkstyle This closes #4105 Signed-off-by: Mike Thomsen --- .../web/api/dto/search/SearchResultsDTO.java | 15 +++ .../controller/ControllerSearchService.java | 55 +++++++++ .../ControllerSearchServiceTest.java | 111 ++++++++++++++++++ .../nf-ng-canvas-flow-status-controller.js | 8 ++ 4 files changed, 189 insertions(+) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/SearchResultsDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/SearchResultsDTO.java index 19462617c9..ae3548a240 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/SearchResultsDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/SearchResultsDTO.java @@ -36,6 +36,7 @@ public class SearchResultsDTO { private List remoteProcessGroupResults = new ArrayList<>(); private List funnelResults = new ArrayList<>(); private List labelResults = new ArrayList<>(); + private List controllerServiceNodeResults = new ArrayList<>(); private List parameterContextResults = new ArrayList<>(); private List parameterResults = new ArrayList<>(); @@ -151,6 +152,20 @@ public class SearchResultsDTO { this.labelResults = labelResults; } + /** + * @return the controller service nodes that matched the search + */ + @ApiModelProperty( + value = "The controller service nodes that matched the search" + ) + public List getControllerServiceNodeResults() { + return controllerServiceNodeResults; + } + + public void setControllerServiceNodeResults(List controllerServiceNodeResults) { + this.controllerServiceNodeResults = controllerServiceNodeResults; + } + /** * @return parameter contexts that matched the search. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerSearchService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerSearchService.java index a41288e44d..7204762a96 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerSearchService.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerSearchService.java @@ -33,6 +33,7 @@ import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.label.Label; import org.apache.nifi.controller.queue.FlowFileQueue; +import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.flowfile.FlowFilePrioritizer; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.groups.RemoteProcessGroup; @@ -175,13 +176,67 @@ public class ControllerSearchService { } } + for (final ControllerServiceNode controllerServiceNode : group.getControllerServices(false)) { + if (controllerServiceNode.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, controllerServiceNode); + if (match != null) { + match.setGroupId(group.getIdentifier()); + match.setParentGroup(buildResultGroup(group, user)); + match.setVersionedGroup(buildVersionedGroup(group, user)); + results.getControllerServiceNodeResults().add(match); + } + } + } + for (final ProcessGroup processGroup : group.getProcessGroups()) { search(results, search, processGroup); } } + /** + * Searches controller service for the given search term + * + * @param search the search term + * @param controllerServiceNode a group controller service node + */ + private ComponentSearchResultDTO search(final String search, final ControllerServiceNode controllerServiceNode) { + final List matches = new ArrayList<>(); + addIfAppropriate(search, controllerServiceNode.getIdentifier(), "Id", matches); + addIfAppropriate(search, controllerServiceNode.getVersionedComponentId().orElse(null), "Version Control ID", matches); + addIfAppropriate(search, controllerServiceNode.getName(), "Name", matches); + addIfAppropriate(search, controllerServiceNode.getComments(), "Comments", matches); + + // search property values + controllerServiceNode.getRawPropertyValues().forEach((property, propertyValue) -> { + addIfAppropriate(search, property.getName(), "Property Name", matches); + addIfAppropriate(search, property.getDescription(), "Property Description", matches); + + // never include sensitive properties in search results + if (property.isSensitive()) { + return; + } + + if (propertyValue != null) { + addIfAppropriate(search, propertyValue, "Property Value", matches); + } else { + addIfAppropriate(search, property.getDefaultValue(), "Property Value", matches); + } + }); + + if (matches.isEmpty()) { + return null; + } + + final ComponentSearchResultDTO dto = new ComponentSearchResultDTO(); + dto.setId(controllerServiceNode.getIdentifier()); + dto.setName(controllerServiceNode.getName()); + dto.setMatches(matches); + return dto; + } + /** * Searches all parameter contexts and parameters + * * @param results Search results * @param search The search term */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/controller/ControllerSearchServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/controller/ControllerSearchServiceTest.java index 74ffc1c20c..66ed8a9eac 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/controller/ControllerSearchServiceTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/controller/ControllerSearchServiceTest.java @@ -19,11 +19,15 @@ package org.apache.nifi.web.controller; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.FlowController; import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.StandardProcessorNode; import org.apache.nifi.controller.flow.FlowManager; import org.apache.nifi.controller.label.Label; +import org.apache.nifi.controller.service.ControllerServiceNode; +import org.apache.nifi.controller.service.StandardControllerServiceNode; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.parameter.Parameter; import org.apache.nifi.parameter.ParameterContext; @@ -50,6 +54,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -420,6 +425,86 @@ public class ControllerSearchServiceTest { assertTrue(searchResultsDTO.getLabelResults().get(0).getName().equals("Value for label foo")); } + @Test + public void testSearchControllerServices() { + final ProcessGroup rootProcessGroup = setupMockedProcessGroup("root", null, true, variableRegistry, null); + + final String controllerServiceName = "controllerServiceName"; + final String controllerServiceId = controllerServiceName + "Id"; + + final Map props = new HashMap<>(); + final PropertyDescriptor prop1 = new PropertyDescriptor.Builder() + .name("prop1-name") + .displayName("prop1-displayname") + .description("prop1 description") + .defaultValue("prop1-default") + .build(); + final PropertyDescriptor prop2 = new PropertyDescriptor.Builder() + .name("prop2-name") + .displayName("prop2-displayname") + .description("prop2 description") + .defaultValue("prop2-default") + .build(); + props.put(prop1, "prop1-value"); + props.put(prop2, null); + + setupMockedControllerService(controllerServiceName, rootProcessGroup, true, props); + + // search for name + service.search(searchResultsDTO, "controllerserv", rootProcessGroup); + + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // search for comments + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "foo comment", rootProcessGroup); + + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // search for properties + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "prop1-name", rootProcessGroup); + + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // by default + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "prop2-def", rootProcessGroup); + + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // by description + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "desc", rootProcessGroup); + + // "desc" would typically match both props, but it's for the same controller service. + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // by specified value + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "prop1-value", rootProcessGroup); + + assertEquals(1, searchResultsDTO.getControllerServiceNodeResults().size()); + assertEquals(controllerServiceId, searchResultsDTO.getControllerServiceNodeResults().get(0).getId()); + assertEquals(controllerServiceName, searchResultsDTO.getControllerServiceNodeResults().get(0).getName()); + + // search finding no match + searchResultsDTO = new SearchResultsDTO(); + service.search(searchResultsDTO, "ZZZZZZZZZYYYYYY", rootProcessGroup); + + assertEquals(0, searchResultsDTO.getControllerServiceNodeResults().size()); + } + /** * Mocks Labels including isAuthorized() and their identifier and value * @@ -512,6 +597,32 @@ public class ControllerSearchServiceTest { }).when(containingProcessGroup).getProcessors(); } + private static void setupMockedControllerService(final String controllerServiceName, final ProcessGroup containingProcessGroup, boolean authorizedToRead, + Map properties) { + final String controllerServiceId = controllerServiceName + "Id"; + final ControllerService controllerService = mock(ControllerService.class); + + final ControllerServiceNode controllerServiceNode1 = mock(StandardControllerServiceNode.class); + Mockito.doReturn(authorizedToRead).when(controllerServiceNode1) + .isAuthorized(AdditionalMatchers.or(any(Authorizer.class), isNull()), eq(RequestAction.READ), AdditionalMatchers.or(any(NiFiUser.class), isNull())); + Mockito.doReturn(controllerService).when(controllerServiceNode1).getControllerServiceImplementation(); + // set controller service node attributes + Mockito.doReturn(controllerServiceId).when(controllerServiceNode1).getIdentifier(); + Mockito.doReturn(controllerServiceName).when(controllerServiceNode1).getName(); + Mockito.doReturn(Optional.ofNullable(null)).when(controllerServiceNode1).getVersionedComponentId(); + Mockito.doReturn("foo comments").when(controllerServiceNode1).getComments(); + + //set properties + Mockito.doReturn(properties).when(controllerServiceNode1).getRawPropertyValues(); + + // assign controller service node to its PG + Mockito.doReturn(new HashSet() { + { + add(controllerServiceNode1); + } + }).when(containingProcessGroup).getControllerServices(anyBoolean()); + } + /** * Mocks ProcessGroup due to isAuthorized(). The final class StandardProcessGroup can't be used. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js index 357ad49255..61494fff4a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js @@ -208,6 +208,14 @@ }); } + // show all controller services + if (!nfCommon.isEmpty(searchResults.controllerServiceNodeResults)) { + ul.append('
  • Controller Services
  • '); + $.each(searchResults.controllerServiceNodeResults, function (i, controllerServiceMatch) { + nfSearchAutocomplete._renderItem(ul, $.extend({}, controllerServiceMatch, { type: 'controller service' })); + }); + } + // show all parameter contexts and parameters if (!nfCommon.isEmpty(searchResults.parameterContextResults)) { ul.append('
  • Parameter Contexts
  • ');