You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2025/12/01 15:28:30 UTC

(nifi) branch main updated: NIFI-15251 Validate empty Parameters referenced in UpdateAttribute Dynamic Properties (#10577)

This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new ab88405c20 NIFI-15251 Validate empty Parameters referenced in UpdateAttribute Dynamic Properties (#10577)
ab88405c20 is described below

commit ab88405c20e5cc8a1fbdf87fdd34c6918e6477ae
Author: Pierre Villard <pi...@gmail.com>
AuthorDate: Mon Dec 1 16:28:25 2025 +0100

    NIFI-15251 Validate empty Parameters referenced in UpdateAttribute Dynamic Properties (#10577)
    
    Signed-off-by: David Handermann <ex...@apache.org>
---
 .../nifi/processors/attributes/UpdateAttribute.java | 21 +++++++++++++++++++++
 .../nifi/update/attributes/TestUpdateAttribute.java | 12 ++++++++++++
 .../org/apache/nifi/util/MockProcessContext.java    | 10 ++++++++++
 .../org/apache/nifi/util/MockValidationContext.java | 12 ++++++++----
 4 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/main/java/org/apache/nifi/processors/attributes/UpdateAttribute.java b/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/main/java/org/apache/nifi/processors/attributes/UpdateAttribute.java
index 378a720528..78832d5360 100644
--- a/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/main/java/org/apache/nifi/processors/attributes/UpdateAttribute.java
+++ b/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/main/java/org/apache/nifi/processors/attributes/UpdateAttribute.java
@@ -336,6 +336,27 @@ public class UpdateAttribute extends AbstractProcessor implements Searchable {
     protected Collection<ValidationResult> customValidate(final ValidationContext context) {
         final List<ValidationResult> reasons = new ArrayList<>(super.customValidate(context));
 
+        for (final Map.Entry<PropertyDescriptor, String> entry : context.getProperties().entrySet()) {
+            final PropertyDescriptor descriptor = entry.getKey();
+            if (descriptor.isDynamic()) {
+                final Collection<String> referencedParameters = context.getReferencedParameters(descriptor.getName());
+                final List<String> unsetParameters = referencedParameters.stream()
+                        .filter(parameterName -> !context.isParameterDefined(parameterName) || !context.isParameterSet(parameterName))
+                        .toList();
+                if (!unsetParameters.isEmpty()) {
+                    final String evaluated = context.newPropertyValue(entry.getValue()).evaluateAttributeExpressions().getValue();
+                    if (StringUtils.isEmpty(evaluated)) {
+                        reasons.add(new ValidationResult.Builder()
+                                .subject(descriptor.getDisplayName())
+                                .valid(false)
+                                .explanation("Dynamic Property references a Parameter that is not set: " + referencedParameters.iterator().next())
+                                .build());
+                        continue;
+                    }
+                }
+            }
+        }
+
         if (!context.getProperty(STORE_STATE).getValue().equals(DO_NOT_STORE_STATE)) {
             String initValue = context.getProperty(STATEFUL_VARIABLES_INIT_VALUE).getValue();
             if (initValue == null) {
diff --git a/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/test/java/org/apache/nifi/update/attributes/TestUpdateAttribute.java b/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/test/java/org/apache/nifi/update/attributes/TestUpdateAttribute.java
index ac25a2c67f..b07b8098ab 100644
--- a/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/test/java/org/apache/nifi/update/attributes/TestUpdateAttribute.java
+++ b/nifi-extension-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/src/test/java/org/apache/nifi/update/attributes/TestUpdateAttribute.java
@@ -102,6 +102,18 @@ public class TestUpdateAttribute {
         runner.assertNotValid();
     }
 
+    @Test
+    public void testDynamicPropertyWithUnsetParameter() {
+        runner.setProperty("attribute.with.param", "#{missing.parameter}");
+        runner.assertNotValid();
+        runner.setProperty("attribute.with.param", "#{missing.parameter}#{another.missing.parameter}");
+        runner.assertNotValid();
+        runner.setProperty("attribute.with.param", "#{missing.parameter} #{another.missing.parameter}");
+        runner.assertValid();
+        runner.setProperty("attribute.with.param", "#{missing.parameter}${now()}");
+        runner.assertValid();
+    }
+
     @Test
     public void testBasicState() {
         runner.setProperty(UpdateAttribute.STORE_STATE, STORE_STATE_LOCALLY);
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
index b3fc283f2d..29d37496b5 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
@@ -77,6 +77,7 @@ public class MockProcessContext extends MockControllerServiceLookup implements P
 
     // This is only for testing purposes as we don't want to set env/sys variables in the tests
     private final Map<String, String> environmentVariables;
+    private final Map<String, String> contextParameters;
 
     private final ParameterLookup parameterLookup;
 
@@ -157,9 +158,18 @@ public class MockProcessContext extends MockControllerServiceLookup implements P
         this.inputRequirement = component.getClass().getAnnotation(InputRequirement.class);
         this.stateManager = stateManager;
         this.environmentVariables = environmentVariables;
+        this.contextParameters = contextParameters == null ? Collections.emptyMap() : contextParameters;
         this.parameterLookup = contextParameters != null ? new MockParameterLookup(contextParameters) : ParameterLookup.EMPTY;
     }
 
+    Map<String, String> getContextParameters() {
+        return contextParameters;
+    }
+
+    ParameterLookup getParameterLookup() {
+        return parameterLookup;
+    }
+
 
     @Override
     public PropertyValue getProperty(final PropertyDescriptor descriptor) {
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
index e91928a99b..87e78eab73 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
@@ -59,7 +59,7 @@ public class MockValidationContext extends MockControllerServiceLookup implement
     public MockValidationContext(final MockProcessContext processContext, final StateManager stateManager, final ParameterLookup parameterLookup) {
         this.context = processContext;
         this.stateManager = stateManager;
-        this.parameterLookup = parameterLookup != null ? parameterLookup : ParameterLookup.EMPTY;
+        this.parameterLookup = parameterLookup != null ? parameterLookup : processContext.getParameterLookup();
 
         final Map<PropertyDescriptor, String> properties = processContext.getProperties();
         expressionLanguageSupported = new HashMap<>(properties.size());
@@ -179,7 +179,9 @@ public class MockValidationContext extends MockControllerServiceLookup implement
 
     @Override
     public Collection<String> getReferencedParameters(final String propertyName) {
-        final String rawPropertyValue = context.getProperty(propertyName).getValue();
+        final PropertyDescriptor descriptor = new PropertyDescriptor.Builder().name(propertyName).build();
+        final PropertyValue propertyValue = context.getPropertyWithoutValidatingExpressions(descriptor);
+        final String rawPropertyValue = propertyValue == null ? null : propertyValue.getValue();
         final boolean elSupported = isExpressionLanguageSupported(propertyName);
 
         final ParameterParser parser = elSupported ? new ExpressionLanguageAwareParameterParser() : new ExpressionLanguageAgnosticParameterParser();
@@ -192,12 +194,14 @@ public class MockValidationContext extends MockControllerServiceLookup implement
 
     @Override
     public boolean isParameterDefined(final String parameterName) {
-        return true;
+        final Map<String, String> contextParameters = context.getContextParameters();
+        return contextParameters.containsKey(parameterName);
     }
 
     @Override
     public boolean isParameterSet(final String parameterName) {
-        return true;
+        final Map<String, String> contextParameters = context.getContextParameters();
+        return contextParameters.containsKey(parameterName) && contextParameters.get(parameterName) != null;
     }
 
 }