turcsanyip 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_r392362628
##########
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,633 +16,214 @@
*/
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.query.SearchQuery;
+import
org.apache.nifi.web.search.resultenrichment.ComponentSearchResultEnricher;
+import
org.apache.nifi.web.search.resultenrichment.ComponentSearchResultEnricherFactory;
-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;
+ private ComponentSearchResultEnricherFactory resultEnricherFactory;
+
+ 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 term in the controller beginning from a given process group.
- *
+ * Searches all parameter contexts and parameters.
+ * @param searchQuery The search term
* @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);
- }
- }
+ 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 {
+ searchInProcessGroup(results, searchQuery,
searchQuery.getRootGroup());
}
+ }
- 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);
- }
- }
- }
+ private void searchInProcessGroup(final SearchResultsDTO results, final
SearchQuery searchQuery, final ProcessGroup scope) {
+ final NiFiUser user = searchQuery.getUser();
+ final ComponentSearchResultEnricher resultEnricher =
resultEnricherFactory.getComponentResultEnricher(scope, user);
+ final ComponentSearchResultEnricher groupResultErnicher =
resultEnricherFactory.getProcessGroupResultEnricher(scope, user);
- 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);
- }
- }
+ if (appliesToGroupFilter(searchQuery, scope)) {
+ searchComponentType(Collections.singletonList(scope), user,
searchQuery, matcherForProcessGroup, groupResultErnicher,
results.getProcessGroupResults());
+ searchComponentType(scope.getProcessors(), user, searchQuery,
matcherForProcessor, resultEnricher, results.getProcessorResults());
+ searchComponentType(scope.getConnections(), user, searchQuery,
matcherForConnection, resultEnricher, results.getConnectionResults());
+ searchComponentType(scope.getRemoteProcessGroups(), user,
searchQuery, matcherForRemoteProcessGroup, resultEnricher,
results.getRemoteProcessGroupResults());
+ searchComponentType(scope.getInputPorts(), user, searchQuery,
matcherForPort, resultEnricher, results.getInputPortResults());
+ searchComponentType(scope.getOutputPorts(), user, searchQuery,
matcherForPort, resultEnricher, results.getOutputPortResults());
+ searchComponentType(scope.getFunnels(), user, searchQuery,
matcherForFunnel, resultEnricher, results.getFunnelResults());
+ searchComponentType(scope.getLabels(), user, searchQuery,
matcherForLabel, resultEnricher, results.getLabelResults());
}
- 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);
- }
- }
- }
+ scope.getProcessGroups().forEach(processGroup ->
searchInProcessGroup(results, searchQuery, processGroup));
+ }
- for (final ProcessGroup processGroup : group.getProcessGroups()) {
- search(results, search, processGroup);
- }
+ private boolean appliesToGroupFilter(final SearchQuery searchQuery, final
ProcessGroup scope) {
+ return !searchQuery.hasFilter(FILTER_NAME_GROUP) ||
eligibleForGroupFilter(scope, searchQuery.getFilter(FILTER_NAME_GROUP));
}
/**
- * Searches all parameter contexts and parameters
- * @param results Search results
- * @param search The search term
+ * Check is the group is eligible for the filter value. It might be
eligible based on name or id.
+ *
+ * @param scope The subject process group.
+ * @param filterValue The value to match against.
+ *
+ * @return True in case the scope process group or any parent is matching.
A group is matching when it's name or it's id contains the filter value.
*/
- 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");
- }
- } 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);
- }
+ private boolean eligibleForGroupFilter(final ProcessGroup scope, final
String filterValue) {
+ final List<ProcessGroup> lineage = getLineage(scope);
- // group access controls
- for (final String groupAccessControl :
publicPort.getGroupAccessControl()) {
- addIfAppropriate(searchStr, groupAccessControl, "Group access
control", matches);
+ for (final ProcessGroup group : lineage) {
+ if (StringUtils.containsIgnoreCase(group.getName(), filterValue)
|| StringUtils.containsIgnoreCase(group.getIdentifier(), filterValue)) {
+ return true;
}
}
- if (matches.isEmpty()) {
- return null;
- }
-
- final ComponentSearchResultDTO dto = new ComponentSearchResultDTO();
- dto.setId(port.getIdentifier());
- dto.setName(port.getName());
- dto.setMatches(matches);
- return dto;
+ return false;
}
- 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");
- }
+ private List<ProcessGroup> getLineage(final ProcessGroup group) {
+ final LinkedList<ProcessGroup> result = new LinkedList<>();
+ result.add(group);
+ ProcessGroup current = group;
- // consider execution node
- if (ExecutionNode.PRIMARY.equals(procNode.getExecutionNode()) &&
StringUtils.containsIgnoreCase("primary", searchStr)) {
- matches.add("Execution node: primary");
+ while (current.getParent() != null) {
+ current = current.getParent();
+ result.addLast(current);
}
- // 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 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 <T extends Authorizable> void searchComponentType(
+ final Collection<T> components,
+ final NiFiUser user,
+ final SearchQuery searchQuery,
+ final ComponentMatcher<T> matcher,
+ final ComponentSearchResultEnricher matchEnriching,
Review comment:
matchEnriching => (result)Enricher ?
----------------------------------------------------------------
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:
[email protected]
With regards,
Apache Git Services