Skip to content

Commit

Permalink
NIFI-12832: Eliminated unnecessary dependencies from nifi-mock; moved…
Browse files Browse the repository at this point in the history
… StandardValidators to nifi-api; broke apart FormatUtils into FormatUtils and TimeFormat classes, with TimeFormat existing in nifi-api
  • Loading branch information
markap14 committed Feb 21, 2024
1 parent 6c76eca commit c305910
Show file tree
Hide file tree
Showing 71 changed files with 1,944 additions and 1,347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.controller.ControllerServiceLookup;
import org.apache.nifi.expression.ExpressionLanguageCompiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

public interface ValidationContext extends PropertyContext {
Expand Down Expand Up @@ -132,7 +136,68 @@ public interface ValidationContext extends PropertyContext {
* @param propertyDescriptorLookup a lookup for converting from a property name to the property descriptor with that name
* @return <code>true</code> if all dependencies of the given property descriptor are satisfied, <code>false</code> otherwise
*/
boolean isDependencySatisfied(PropertyDescriptor propertyDescriptor, Function<String, PropertyDescriptor> propertyDescriptorLookup);
default boolean isDependencySatisfied(PropertyDescriptor propertyDescriptor, Function<String, PropertyDescriptor> propertyDescriptorLookup) {
return isDependencySatisfied(propertyDescriptor, propertyDescriptorLookup, new HashSet<>());
}

private boolean isDependencySatisfied(final PropertyDescriptor propertyDescriptor, final Function<String, PropertyDescriptor> propertyDescriptorLookup, final Set<String> propertiesSeen) {
final Logger logger = LoggerFactory.getLogger(ValidationContext.class);

final Set<PropertyDependency> dependencies = propertyDescriptor.getDependencies();
if (dependencies.isEmpty()) {
logger.debug("Dependency for {} is satisfied because it has no dependencies", propertyDescriptor);
return true;
}

final boolean added = propertiesSeen.add(propertyDescriptor.getName());
if (!added) {
logger.debug("Dependency for {} is not satisifed because its dependency chain contains a loop: {}", propertyDescriptor, propertiesSeen);
return false;
}

try {
for (final PropertyDependency dependency : dependencies) {
final String dependencyName = dependency.getPropertyName();

// Check if the property being depended upon has its dependencies satisfied.
final PropertyDescriptor dependencyDescriptor = propertyDescriptorLookup.apply(dependencyName);
if (dependencyDescriptor == null) {
logger.debug("Dependency for {} is not satisfied because it has a dependency on {}, which has no property descriptor", propertyDescriptor, dependencyName);
return false;
}

final PropertyValue propertyValue = getProperty(dependencyDescriptor);
final String dependencyValue = propertyValue == null ? dependencyDescriptor.getDefaultValue() : propertyValue.getValue();
if (dependencyValue == null) {
logger.debug("Dependency for {} is not satisfied because it has a dependency on {}, which has a null value", propertyDescriptor, dependencyName);
return false;
}

final boolean transitiveDependencySatisfied = isDependencySatisfied(dependencyDescriptor, propertyDescriptorLookup, propertiesSeen);
if (!transitiveDependencySatisfied) {
logger.debug("Dependency for {} is not satisfied because it has a dependency on {} and {} does not have its dependencies satisfied",
propertyDescriptor, dependencyName, dependencyName);
return false;
}

// Check if the property being depended upon is set to one of the values that satisfies this dependency.
// If the dependency has no dependent values, then any non-null value satisfies the dependency.
// The value is already known to be non-null due to the check above.
final Set<String> dependentValues = dependency.getDependentValues();
if (dependentValues != null && !dependentValues.contains(dependencyValue)) {
logger.debug("Dependency for {} is not satisfied because it depends on {}, which has a value of {}. Dependent values = {}",
propertyDescriptor, dependencyName, dependencyValue, dependentValues);
return false;
}
}

logger.debug("All dependencies for {} are satisfied", propertyDescriptor);

return true;
} finally {
propertiesSeen.remove(propertyDescriptor.getName());
}
}

/**
* Determines whether or not incoming and outgoing connections should be validated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
*/
package org.apache.nifi.processor.util;

import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.time.TimeFormat;

import java.io.File;
import java.net.URI;
import java.nio.charset.Charset;
Expand All @@ -27,15 +36,6 @@
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.UriUtils;

public class StandardValidators {

Expand Down Expand Up @@ -463,7 +463,7 @@ private static boolean isEmpty(final String value) {
}

public static final Validator TIME_PERIOD_VALIDATOR = new Validator() {
private final Pattern TIME_DURATION_PATTERN = Pattern.compile(FormatUtils.TIME_DURATION_REGEX);
private final Pattern TIME_DURATION_PATTERN = Pattern.compile(TimeFormat.TIME_DURATION_REGEX);

@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
Expand Down Expand Up @@ -540,9 +540,9 @@ public ValidationResult validate(final String subject, final String input, final
}

try {
// Check that we can parse the value as a URL
final String evaluatedInput = context.newPropertyValue(input).evaluateAttributeExpressions().getValue();
final URI uri = UriUtils.create(evaluatedInput);
uri.toURL();
URI.create(evaluatedInput).toURL();
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid URL").valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid URL").valid(false).build();
Expand Down Expand Up @@ -815,7 +815,7 @@ public ValidationResult validate(final String subject, final String input, final
//
//
static class TimePeriodValidator implements Validator {
private static final Pattern pattern = Pattern.compile(FormatUtils.TIME_DURATION_REGEX);
private static final Pattern pattern = Pattern.compile(TimeFormat.TIME_DURATION_REGEX);

private final long minNanos;
private final long maxNanos;
Expand Down Expand Up @@ -843,7 +843,7 @@ public ValidationResult validate(final String subject, final String input, final
final boolean validSyntax = pattern.matcher(lowerCase).matches();
final ValidationResult.Builder builder = new ValidationResult.Builder();
if (validSyntax) {
final long nanos = FormatUtils.getTimeDuration(lowerCase, TimeUnit.NANOSECONDS);
final long nanos = new TimeFormat().getTimeDuration(lowerCase, TimeUnit.NANOSECONDS);

if (nanos < minNanos || nanos > maxNanos) {
builder.subject(subject).input(input).valid(false)
Expand Down
Loading

0 comments on commit c305910

Please sign in to comment.