tpalfy commented on a change in pull request #4123: NIFI-7188: Adding filter 
capabilities into search & prerequisite refactors
URL: https://github.com/apache/nifi/pull/4123#discussion_r390517409
 
 

 ##########
 File path: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerSearchService.java
 ##########
 @@ -16,564 +16,162 @@
  */
 package org.apache.nifi.web.controller;
 
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.authorization.Authorizer;
 import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.user.NiFiUser;
-import org.apache.nifi.authorization.user.NiFiUserUtils;
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.validation.ValidationStatus;
-import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Funnel;
 import org.apache.nifi.connectable.Port;
 import org.apache.nifi.controller.FlowController;
 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.flowfile.FlowFilePrioritizer;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroup;
-import org.apache.nifi.nar.NarCloseable;
 import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterContext;
-import org.apache.nifi.parameter.ParameterContextManager;
-import org.apache.nifi.processor.DataUnit;
-import org.apache.nifi.processor.Processor;
-import org.apache.nifi.processor.Relationship;
-import org.apache.nifi.registry.ComponentVariableRegistry;
-import org.apache.nifi.registry.VariableDescriptor;
-import org.apache.nifi.registry.VariableRegistry;
-import org.apache.nifi.remote.PublicPort;
-import org.apache.nifi.scheduling.ExecutionNode;
-import org.apache.nifi.scheduling.SchedulingStrategy;
-import org.apache.nifi.search.SearchContext;
-import org.apache.nifi.search.SearchResult;
-import org.apache.nifi.search.Searchable;
 import org.apache.nifi.web.api.dto.search.ComponentSearchResultDTO;
 import org.apache.nifi.web.api.dto.search.SearchResultGroupDTO;
 import org.apache.nifi.web.api.dto.search.SearchResultsDTO;
+import org.apache.nifi.web.search.ComponentMatcher;
+import org.apache.nifi.web.search.MatchEnriching;
+import org.apache.nifi.web.search.query.SearchQuery;
 
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * NiFi web controller's helper service that implements component search.
  */
 public class ControllerSearchService {
+    private final static String FILTER_NAME_GROUP = "group";
+    private final static String FILTER_NAME_SCOPE = "scope";
+    private final static String FILTER_SCOPE_VALUE_HERE = "here";
+
     private FlowController flowController;
     private Authorizer authorizer;
-    private VariableRegistry variableRegistry;
-
-    /**
-     * Searches term in the controller beginning from a given process group.
-     *
-     * @param results Search results
-     * @param search  The search term
-     * @param group   The init process group
-     */
-    public void search(final SearchResultsDTO results, final String search, 
final ProcessGroup group) {
-        final NiFiUser user = NiFiUserUtils.getNiFiUser();
-
-        if (group.isAuthorized(authorizer, RequestAction.READ, user)) {
-            final ComponentSearchResultDTO groupMatch = search(search, group);
-            if (groupMatch != null) {
-                // get the parent group, not the current one
-                groupMatch.setParentGroup(buildResultGroup(group.getParent(), 
user));
-                
groupMatch.setVersionedGroup(buildVersionedGroup(group.getParent(), user));
-                results.getProcessGroupResults().add(groupMatch);
-            }
-        }
-
-        for (final ProcessorNode procNode : group.getProcessors()) {
-            if (procNode.isAuthorized(authorizer, RequestAction.READ, user)) {
-                final ComponentSearchResultDTO match = search(search, 
procNode);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getProcessorResults().add(match);
-                }
-            }
-        }
-
-        for (final Connection connection : group.getConnections()) {
-            if (connection.isAuthorized(authorizer, RequestAction.READ, user)) 
{
-                final ComponentSearchResultDTO match = search(search, 
connection);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getConnectionResults().add(match);
-                }
-            }
-        }
-
-        for (final RemoteProcessGroup remoteGroup : 
group.getRemoteProcessGroups()) {
-            if (remoteGroup.isAuthorized(authorizer, RequestAction.READ, 
user)) {
-                final ComponentSearchResultDTO match = search(search, 
remoteGroup);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getRemoteProcessGroupResults().add(match);
-                }
-            }
-        }
-
-        for (final Port port : group.getInputPorts()) {
-            if (port.isAuthorized(authorizer, RequestAction.READ, user)) {
-                final ComponentSearchResultDTO match = search(search, port);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getInputPortResults().add(match);
-                }
-            }
-        }
-
-        for (final Port port : group.getOutputPorts()) {
-            if (port.isAuthorized(authorizer, RequestAction.READ, user)) {
-                final ComponentSearchResultDTO match = search(search, port);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getOutputPortResults().add(match);
-                }
-            }
-        }
-
-        for (final Funnel funnel : group.getFunnels()) {
-            if (funnel.isAuthorized(authorizer, RequestAction.READ, user)) {
-                final ComponentSearchResultDTO match = search(search, funnel);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getFunnelResults().add(match);
-                }
-            }
-        }
-
-        for (final Label label : group.getLabels()) {
-            if (label.isAuthorized(authorizer, RequestAction.READ, user)) {
-                final ComponentSearchResultDTO match = search(search, label);
-                if (match != null) {
-                    match.setGroupId(group.getIdentifier());
-                    match.setParentGroup(buildResultGroup(group, user));
-                    match.setVersionedGroup(buildVersionedGroup(group, user));
-                    results.getLabelResults().add(match);
-                }
-            }
-        }
 
-        for (final ProcessGroup processGroup : group.getProcessGroups()) {
-            search(results, search, processGroup);
-        }
-    }
+    private ComponentMatcher<ProcessorNode> matcherForProcessor;
+    private ComponentMatcher<ProcessGroup> matcherForProcessGroup;
+    private ComponentMatcher<Connection> matcherForConnection;
+    private ComponentMatcher<RemoteProcessGroup> matcherForRemoteProcessGroup;
+    private ComponentMatcher<Port> matcherForPort;
+    private ComponentMatcher<Funnel> matcherForFunnel;
+    private ComponentMatcher<ParameterContext> matcherForParameterContext;
+    private ComponentMatcher<Parameter> matcherForParameter;
+    private ComponentMatcher<Label> matcherForLabel;
 
     /**
-     * Searches all parameter contexts and parameters
+     * Searches all parameter contexts and parameters.
+     * @param searchQuery  The search term
      * @param results Search results
-     * @param search  The search term
      */
-    public void searchParameters(final SearchResultsDTO results, final String 
search) {
-        final NiFiUser user = NiFiUserUtils.getNiFiUser();
-        ParameterContextManager parameterContextManager = 
flowController.getFlowManager().getParameterContextManager();
-
-        final Set<ParameterContext> parameterContexts = 
parameterContextManager.getParameterContexts();
-        for (final ParameterContext parameterContext : parameterContexts) {
-            if (parameterContext.isAuthorized(authorizer, RequestAction.READ, 
user)) {
-                ComponentSearchResultDTO parameterContextMatch = 
search(search, parameterContext);
-                if (parameterContextMatch != null) {
-                    
results.getParameterContextResults().add(parameterContextMatch);
-                }
-
-                // search each parameter within the context as well
-                for (Parameter parameter : 
parameterContext.getParameters().values()) {
-                    ComponentSearchResultDTO parameterMatch = search(search, 
parameter);
-                    if (parameterMatch != null) {
-                        final SearchResultGroupDTO paramContextGroup = new 
SearchResultGroupDTO();
-                        
paramContextGroup.setId(parameterContext.getIdentifier());
-                        paramContextGroup.setName(parameterContext.getName());
-                        parameterMatch.setParentGroup(paramContextGroup);
-
-                        results.getParameterResults().add(parameterMatch);
-                    }
-                }
-            }
-        }
-    }
-
-    private ComponentSearchResultDTO search(final String searchStr, final Port 
port) {
-        final List<String> matches = new ArrayList<>();
-
-        addIfAppropriate(searchStr, port.getIdentifier(), "Id", matches);
-        addIfAppropriate(searchStr, 
port.getVersionedComponentId().orElse(null), "Version Control ID", matches);
-        addIfAppropriate(searchStr, port.getName(), "Name", matches);
-        addIfAppropriate(searchStr, port.getComments(), "Comments", matches);
-
-        // consider scheduled state
-        if (ScheduledState.DISABLED.equals(port.getScheduledState())) {
-            if (StringUtils.containsIgnoreCase("disabled", searchStr)) {
-                matches.add("Run status: Disabled");
-            }
+    public void search(final SearchQuery searchQuery, final SearchResultsDTO 
results) {
+        if (searchQuery.hasFilter(FILTER_NAME_SCOPE) && 
FILTER_SCOPE_VALUE_HERE.equals(searchQuery.getFilter(FILTER_NAME_SCOPE))) {
+            searchInProcessGroup(results, searchQuery, 
searchQuery.getActiveGroup());
         } else {
-            if (StringUtils.containsIgnoreCase("invalid", searchStr) && 
!port.isValid()) {
-                matches.add("Run status: Invalid");
-            } else if (ScheduledState.RUNNING.equals(port.getScheduledState()) 
&& StringUtils.containsIgnoreCase("running", searchStr)) {
-                matches.add("Run status: Running");
-            } else if (ScheduledState.STOPPED.equals(port.getScheduledState()) 
&& StringUtils.containsIgnoreCase("stopped", searchStr)) {
-                matches.add("Run status: Stopped");
-            }
-        }
-
-        if (port instanceof PublicPort) {
-            final PublicPort publicPort = (PublicPort) port;
-
-            // user access controls
-            for (final String userAccessControl : 
publicPort.getUserAccessControl()) {
-                addIfAppropriate(searchStr, userAccessControl, "User access 
control", matches);
-            }
-
-            // group access controls
-            for (final String groupAccessControl : 
publicPort.getGroupAccessControl()) {
-                addIfAppropriate(searchStr, groupAccessControl, "Group access 
control", matches);
-            }
+            searchInProcessGroup(results, searchQuery, 
searchQuery.getRootGroup());
         }
-
-        if (matches.isEmpty()) {
-            return null;
-        }
-
-        final ComponentSearchResultDTO dto = new ComponentSearchResultDTO();
-        dto.setId(port.getIdentifier());
-        dto.setName(port.getName());
-        dto.setMatches(matches);
-        return dto;
     }
 
-    private ComponentSearchResultDTO search(final String searchStr, final 
ProcessorNode procNode) {
-        final List<String> matches = new ArrayList<>();
-        final Processor processor = procNode.getProcessor();
-
-        addIfAppropriate(searchStr, procNode.getIdentifier(), "Id", matches);
-        addIfAppropriate(searchStr, 
procNode.getVersionedComponentId().orElse(null), "Version Control ID", matches);
-        addIfAppropriate(searchStr, procNode.getName(), "Name", matches);
-        addIfAppropriate(searchStr, procNode.getComments(), "Comments", 
matches);
-
-        // consider scheduling strategy
-        if 
(SchedulingStrategy.EVENT_DRIVEN.equals(procNode.getSchedulingStrategy()) && 
StringUtils.containsIgnoreCase("event", searchStr)) {
-            matches.add("Scheduling strategy: Event driven");
-        } else if 
(SchedulingStrategy.TIMER_DRIVEN.equals(procNode.getSchedulingStrategy()) && 
StringUtils.containsIgnoreCase("timer", searchStr)) {
-            matches.add("Scheduling strategy: Timer driven");
-        } else if 
(SchedulingStrategy.PRIMARY_NODE_ONLY.equals(procNode.getSchedulingStrategy()) 
&& StringUtils.containsIgnoreCase("primary", searchStr)) {
-            // PRIMARY_NODE_ONLY has been deprecated as a SchedulingStrategy 
and replaced by PRIMARY as an ExecutionNode.
-            matches.add("Scheduling strategy: On primary node");
-        }
-
-        // consider execution node
-        if (ExecutionNode.PRIMARY.equals(procNode.getExecutionNode()) && 
StringUtils.containsIgnoreCase("primary", searchStr)) {
-            matches.add("Execution node: primary");
-        }
-
-        // consider scheduled state
-        if (ScheduledState.DISABLED.equals(procNode.getScheduledState())) {
-            if (StringUtils.containsIgnoreCase("disabled", searchStr)) {
-                matches.add("Run status: Disabled");
-            }
-        } else {
-            if (StringUtils.containsIgnoreCase("invalid", searchStr) && 
procNode.getValidationStatus() == ValidationStatus.INVALID) {
-                matches.add("Run status: Invalid");
-            } else if (StringUtils.containsIgnoreCase("validating", searchStr) 
&& procNode.getValidationStatus() == ValidationStatus.VALIDATING) {
-                matches.add("Run status: Validating");
-            } else if 
(ScheduledState.RUNNING.equals(procNode.getScheduledState()) && 
StringUtils.containsIgnoreCase("running", searchStr)) {
-                matches.add("Run status: Running");
-            } else if 
(ScheduledState.STOPPED.equals(procNode.getScheduledState()) && 
StringUtils.containsIgnoreCase("stopped", searchStr)) {
-                matches.add("Run status: Stopped");
-            }
-        }
-
-        for (final Relationship relationship : procNode.getRelationships()) {
-            addIfAppropriate(searchStr, relationship.getName(), 
"Relationship", matches);
-        }
-
-        // Add both the actual class name and the component type. This allows 
us to search for 'Ghost'
-        // to search for components that could not be instantiated.
-        addIfAppropriate(searchStr, processor.getClass().getSimpleName(), 
"Type", matches);
-        addIfAppropriate(searchStr, procNode.getComponentType(), "Type", 
matches);
-
-        for (final Map.Entry<PropertyDescriptor, String> entry : 
procNode.getRawPropertyValues().entrySet()) {
-            final PropertyDescriptor descriptor = entry.getKey();
-
-            addIfAppropriate(searchStr, descriptor.getName(), "Property name", 
matches);
-            addIfAppropriate(searchStr, descriptor.getDescription(), "Property 
description", matches);
-
-            // never include sensitive properties values in search results
-            if (descriptor.isSensitive()) {
-                continue;
-            }
-
-            String value = entry.getValue();
-
-            // if unset consider default value
-            if (value == null) {
-                value = descriptor.getDefaultValue();
-            }
-
-            // evaluate if the value matches the search criteria
-            if (StringUtils.containsIgnoreCase(value, searchStr)) {
-                matches.add("Property value: " + descriptor.getName() + " - " 
+ value);
-            }
-        }
-
-        // consider searching the processor directly
-        if (processor instanceof Searchable) {
-            final Searchable searchable = (Searchable) processor;
-
-            final SearchContext context = new StandardSearchContext(searchStr, 
procNode, flowController.getControllerServiceProvider(), variableRegistry);
-
-            // search the processor using the appropriate thread context 
classloader
-            try (final NarCloseable x = 
NarCloseable.withComponentNarLoader(flowController.getExtensionManager(), 
processor.getClass(), processor.getIdentifier())) {
-                final Collection<SearchResult> searchResults = 
searchable.search(context);
-                if (CollectionUtils.isNotEmpty(searchResults)) {
-                    for (final SearchResult searchResult : searchResults) {
-                        matches.add(searchResult.getLabel() + ": " + 
searchResult.getMatch());
-                    }
-                }
-            } catch (final Throwable t) {
-                // log this as error
-            }
-        }
-
-        if (matches.isEmpty()) {
-            return null;
-        }
-
-        final ComponentSearchResultDTO result = new ComponentSearchResultDTO();
-        result.setId(procNode.getIdentifier());
-        result.setMatches(matches);
-        result.setName(procNode.getName());
-        return result;
+    private void searchInProcessGroup(final SearchResultsDTO results, final 
SearchQuery searchQuery, final ProcessGroup scope) {
+        final NiFiUser user = searchQuery.getUser();
+        final SearchResultGroupDTO parentGroup = buildResultGroup(scope, user);
+        final SearchResultGroupDTO versionedGroup = buildVersionedGroup(scope, 
user);
+        final MatchEnriching matchEnriching = new 
MatchEnriching(scope.getIdentifier(), parentGroup, versionedGroup);
+        final MatchEnriching partialMatchEnriching = new 
MatchEnriching(Optional.empty(), Optional.ofNullable(parentGroup), 
Optional.ofNullable(versionedGroup));
+
+        if (appliesToGroupFilter(searchQuery, scope)) {
+            searchComponentType(Collections.singletonList(scope), user, 
searchQuery, matcherForProcessGroup, partialMatchEnriching, 
results.getProcessGroupResults());
+            searchComponentType(scope.getProcessors(), user, searchQuery, 
matcherForProcessor, matchEnriching, results.getProcessorResults());
+            searchComponentType(scope.getConnections(), user, searchQuery, 
matcherForConnection, matchEnriching, results.getConnectionResults());
+            searchComponentType(scope.getRemoteProcessGroups(), user, 
searchQuery, matcherForRemoteProcessGroup, matchEnriching, 
results.getRemoteProcessGroupResults());
+            searchComponentType(scope.getInputPorts(), user, searchQuery, 
matcherForPort, matchEnriching, results.getInputPortResults());
+            searchComponentType(scope.getOutputPorts(), user, searchQuery, 
matcherForPort, matchEnriching, results.getOutputPortResults());
+            searchComponentType(scope.getFunnels(), user, searchQuery, 
matcherForFunnel, matchEnriching, results.getFunnelResults());
+            searchComponentType(scope.getLabels(), user, searchQuery, 
matcherForLabel, matchEnriching, results.getLabelResults());
+        }
+
+        scope.getProcessGroups().forEach(processGroup -> 
searchInProcessGroup(results, searchQuery, processGroup));
     }
 
-    private ComponentSearchResultDTO search(final String searchStr, final 
ProcessGroup group) {
-        final List<String> matches = new ArrayList<>();
-        final ProcessGroup parent = group.getParent();
-        if (parent == null) {
-            return null;
-        }
-
-        addIfAppropriate(searchStr, group.getIdentifier(), "Id", matches);
-        addIfAppropriate(searchStr, 
group.getVersionedComponentId().orElse(null), "Version Control ID", matches);
-        addIfAppropriate(searchStr, group.getName(), "Name", matches);
-        addIfAppropriate(searchStr, group.getComments(), "Comments", matches);
-
-        final ComponentVariableRegistry varRegistry = 
group.getVariableRegistry();
-        if (varRegistry != null) {
-            final Map<VariableDescriptor, String> variableMap = 
varRegistry.getVariableMap();
-            for (final Map.Entry<VariableDescriptor, String> entry : 
variableMap.entrySet()) {
-                addIfAppropriate(searchStr, entry.getKey().getName(), 
"Variable Name", matches);
-                addIfAppropriate(searchStr, entry.getValue(), "Variable 
Value", matches);
-            }
-        }
-
-        if (matches.isEmpty()) {
-            return null;
-        }
-
-        final ComponentSearchResultDTO result = new ComponentSearchResultDTO();
-        result.setId(group.getIdentifier());
-        result.setName(group.getName());
-        result.setGroupId(parent.getIdentifier());
-        result.setMatches(matches);
-        return result;
+    private boolean appliesToGroupFilter(final SearchQuery searchQuery, final 
ProcessGroup scope) {
+        return searchQuery.hasFilter(FILTER_NAME_GROUP)
 
 Review comment:
   Minor suggestion: The ternary operator expressions are usually harder to 
understand as those with binary ones. Maybe we could use
   ```
   return !searchQuery.hasFilter(FILTER_NAME_GROUP) || 
eligibleForGroupFilter(scope, searchQuery.getFilter(FILTER_NAME_GROUP));
   ```
   

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to