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 <[email protected]>
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 <[email protected]>
---
 .../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;
     }
 
 }

Reply via email to