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;
}
}