This is an automated email from the ASF dual-hosted git repository.
bbende 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 ffa30a17fb NIFI-15010 - FlowDifferenceFilters - better handle property
renaming (#10340)
ffa30a17fb is described below
commit ffa30a17fb83b04abeb90c2cf8ac199a2412df1d
Author: Pierre Villard <[email protected]>
AuthorDate: Wed Sep 24 20:51:06 2025 +0200
NIFI-15010 - FlowDifferenceFilters - better handle property renaming
(#10340)
Signed-off-by: Pierre Villard <[email protected]>
---
.../apache/nifi/util/FlowDifferenceFilters.java | 106 +++++++++++++++++++--
.../nifi/util/TestFlowDifferenceFilters.java | 51 ++++++++++
2 files changed, 148 insertions(+), 9 deletions(-)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/FlowDifferenceFilters.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/FlowDifferenceFilters.java
index c701401e11..caf6ca1ecc 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/FlowDifferenceFilters.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/FlowDifferenceFilters.java
@@ -95,7 +95,8 @@ public class FlowDifferenceFilters {
|| isLogFileSuffixChange(difference)
|| isStaticPropertyRemoved(difference, flowManager)
|| isControllerServiceCreatedForNewProperty(difference,
evaluatedContext)
- || isPropertyParameterizationRename(difference, evaluatedContext);
+ || isPropertyParameterizationRename(difference, evaluatedContext)
+ || isPropertyRenameWithMatchingValue(difference, evaluatedContext);
}
private static boolean isSensitivePropertyDueToGhosting(final
FlowDifference difference, final FlowManager flowManager) {
@@ -585,6 +586,8 @@ public class FlowDifferenceFilters {
final Set<String> serviceIdsReferencedByNewProperties = new
HashSet<>();
final Map<String, List<PropertyDiffInfo>> parameterizedAddsByComponent
= new HashMap<>();
final Map<String, List<PropertyDiffInfo>>
parameterizationRemovalsByComponent = new HashMap<>();
+ final Map<String, List<PropertyDiffInfo>> propertyAddsByComponent =
new HashMap<>();
+ final Map<String, List<PropertyDiffInfo>> propertyRemovalsByComponent
= new HashMap<>();
for (final FlowDifference difference : differences) {
if (difference.getDifferenceType() ==
DifferenceType.PROPERTY_ADDED) {
@@ -602,6 +605,20 @@ public class FlowDifferenceFilters {
}
}
}
+
+ final Optional<String> componentIdOptional =
getComponentInstanceIdentifier(difference);
+ if (componentIdOptional.isPresent()) {
+ final PropertyDiffInfo diffInfo = new
PropertyDiffInfo(getPropertyValue(difference, false), difference);
+
propertyAddsByComponent.computeIfAbsent(componentIdOptional.get(), key -> new
ArrayList<>()).add(diffInfo);
+ }
+ }
+
+ if (difference.getDifferenceType() ==
DifferenceType.PROPERTY_REMOVED) {
+ final Optional<String> componentIdOptional =
getComponentInstanceIdentifier(difference);
+ if (componentIdOptional.isPresent()) {
+ final PropertyDiffInfo diffInfo = new
PropertyDiffInfo(getPropertyValue(difference, true), difference);
+
propertyRemovalsByComponent.computeIfAbsent(componentIdOptional.get(), key ->
new ArrayList<>()).add(diffInfo);
+ }
} else if (difference.getDifferenceType() ==
DifferenceType.PROPERTY_PARAMETERIZED
|| difference.getDifferenceType() ==
DifferenceType.PROPERTY_PARAMETERIZATION_REMOVED) {
@@ -676,14 +693,52 @@ public class FlowDifferenceFilters {
}
}
- final EnvironmentalChangeContext environmentalChangeContext;
- if (serviceIdsWithMatchingAdditions.isEmpty() &&
parameterizedPropertyRenameDifferences.isEmpty()) {
- environmentalChangeContext = EnvironmentalChangeContext.empty();
- } else {
- environmentalChangeContext = new
EnvironmentalChangeContext(serviceIdsWithMatchingAdditions,
parameterizedPropertyRenameDifferences);
+ final Set<FlowDifference> propertyRenamesWithMatchingValues = new
HashSet<>();
+ for (final Map.Entry<String, List<PropertyDiffInfo>> entry :
propertyRemovalsByComponent.entrySet()) {
+ final String componentId = entry.getKey();
+ final List<PropertyDiffInfo> removals = entry.getValue();
+ final List<PropertyDiffInfo> additions = new
ArrayList<>(propertyAddsByComponent.getOrDefault(componentId,
Collections.emptyList()));
+ if (additions.isEmpty()) {
+ continue;
+ }
+
+ for (final PropertyDiffInfo removalInfo : removals) {
+ final Optional<String> removalValue =
removalInfo.propertyValue();
+ if (removalValue.isEmpty()) {
+ continue;
+ }
+
+ PropertyDiffInfo matchingAddition = null;
+ for (final Iterator<PropertyDiffInfo> iterator =
additions.iterator(); iterator.hasNext();) {
+ final PropertyDiffInfo additionInfo = iterator.next();
+ final Optional<String> additionValue =
additionInfo.propertyValue();
+ if (additionValue.isEmpty()) {
+ continue;
+ }
+
+ if (valuesMatch(removalValue, additionValue)) {
+ matchingAddition = additionInfo;
+ iterator.remove();
+ break;
+ }
+ }
+
+ if (matchingAddition != null) {
+ final String removalField =
removalInfo.difference().getFieldName().orElse(null);
+ final String additionField =
matchingAddition.difference().getFieldName().orElse(null);
+ if (!Objects.equals(removalField, additionField)) {
+
propertyRenamesWithMatchingValues.add(removalInfo.difference());
+
propertyRenamesWithMatchingValues.add(matchingAddition.difference());
+ }
+ }
+ }
}
- return environmentalChangeContext;
+ if (serviceIdsWithMatchingAdditions.isEmpty() &&
parameterizedPropertyRenameDifferences.isEmpty() &&
propertyRenamesWithMatchingValues.isEmpty()) {
+ return EnvironmentalChangeContext.empty();
+ }
+
+ return new EnvironmentalChangeContext(serviceIdsWithMatchingAdditions,
parameterizedPropertyRenameDifferences, propertyRenamesWithMatchingValues);
}
public static boolean isControllerServiceCreatedForNewProperty(final
FlowDifference difference, final EnvironmentalChangeContext context) {
@@ -739,6 +794,11 @@ public class FlowDifferenceFilters {
return evaluatedContext.parameterizedPropertyRenames().isEmpty() ?
false : evaluatedContext.parameterizedPropertyRenames().contains(difference);
}
+ public static boolean isPropertyRenameWithMatchingValue(final
FlowDifference difference, final EnvironmentalChangeContext context) {
+ final EnvironmentalChangeContext evaluatedContext =
Objects.requireNonNull(context, "EnvironmentalChangeContext required");
+ return evaluatedContext.propertyRenamesWithMatchingValues().isEmpty()
? false :
evaluatedContext.propertyRenamesWithMatchingValues().contains(difference);
+ }
+
private static Optional<String> getComponentInstanceIdentifier(final
FlowDifference difference) {
final Optional<String> identifierB =
getComponentInstanceIdentifier(difference.getComponentB());
if (identifierB.isPresent()) {
@@ -805,6 +865,27 @@ public class FlowDifferenceFilters {
return parameterReference;
}
+ private static Optional<String> getPropertyValue(final FlowDifference
difference, final boolean fromComponentA) {
+ final Optional<String> fieldNameOptional = difference.getFieldName();
+ if (fieldNameOptional.isEmpty()) {
+ return Optional.empty();
+ }
+
+ final VersionedComponent component = fromComponentA ?
difference.getComponentA() : difference.getComponentB();
+ final Map<String, String> properties = getProperties(component);
+ final String propertyValue = properties.get(fieldNameOptional.get());
+ if (propertyValue != null) {
+ return Optional.of(propertyValue);
+ }
+
+ final Object differenceValue = fromComponentA ? difference.getValueA()
: difference.getValueB();
+ if (differenceValue instanceof String stringValue) {
+ return Optional.of(stringValue);
+ }
+
+ return Optional.empty();
+ }
+
private static Map<String, String> getProperties(final VersionedComponent
component) {
final Map<String, String> properties;
@@ -852,15 +933,18 @@ public class FlowDifferenceFilters {
}
public static final class EnvironmentalChangeContext {
- private static final EnvironmentalChangeContext EMPTY = new
EnvironmentalChangeContext(Collections.emptySet(), Collections.emptySet());
+ private static final EnvironmentalChangeContext EMPTY = new
EnvironmentalChangeContext(Collections.emptySet(), Collections.emptySet(),
Collections.emptySet());
private final Set<String> serviceIdsCreatedForNewProperties;
private final Set<FlowDifference> parameterizedPropertyRenames;
+ private final Set<FlowDifference> propertyRenamesWithMatchingValues;
private EnvironmentalChangeContext(final Set<String>
serviceIdsCreatedForNewProperties,
- final Set<FlowDifference>
parameterizedPropertyRenames) {
+ final Set<FlowDifference>
parameterizedPropertyRenames,
+ final Set<FlowDifference>
propertyRenamesWithMatchingValues) {
this.serviceIdsCreatedForNewProperties =
Collections.unmodifiableSet(new HashSet<>(serviceIdsCreatedForNewProperties));
this.parameterizedPropertyRenames =
Collections.unmodifiableSet(new HashSet<>(parameterizedPropertyRenames));
+ this.propertyRenamesWithMatchingValues =
Collections.unmodifiableSet(new HashSet<>(propertyRenamesWithMatchingValues));
}
static EnvironmentalChangeContext empty() {
@@ -874,5 +958,9 @@ public class FlowDifferenceFilters {
Set<FlowDifference> parameterizedPropertyRenames() {
return parameterizedPropertyRenames;
}
+
+ Set<FlowDifference> propertyRenamesWithMatchingValues() {
+ return propertyRenamesWithMatchingValues;
+ }
}
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/util/TestFlowDifferenceFilters.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/util/TestFlowDifferenceFilters.java
index 31874c94fe..cb2430f570 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/util/TestFlowDifferenceFilters.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/util/TestFlowDifferenceFilters.java
@@ -356,6 +356,57 @@ public class TestFlowDifferenceFilters {
assertTrue(FlowDifferenceFilters.isEnvironmentalChange(parameterized,
null, flowManager, context));
}
+ @Test
+ public void
testPropertyRenameWithMatchingValueObservedAsEnvironmentalChange() {
+ final FlowManager flowManager = Mockito.mock(FlowManager.class);
+ final ProcessorNode processorNode = Mockito.mock(ProcessorNode.class);
+
+ final String processorInstanceId = "processor-instance";
+
+
Mockito.when(flowManager.getProcessorNode(processorInstanceId)).thenReturn(processorNode);
+
+ final String groupId = "group-id";
+ final String versionedId = "versioned-id";
+ final String controllerServiceId = "service-id";
+ final String legacyPropertyName = "box-client-service";
+ final String renamedPropertyName = "Box Client Service";
+
+ final VersionedProcessor versionedProcessor = new VersionedProcessor();
+ versionedProcessor.setComponentType(ComponentType.PROCESSOR);
+ versionedProcessor.setIdentifier(versionedId);
+ versionedProcessor.setProperties(Map.of(legacyPropertyName,
controllerServiceId));
+
+ final InstantiatedVersionedProcessor instantiatedProcessor = new
InstantiatedVersionedProcessor(processorInstanceId, groupId);
+ instantiatedProcessor.setComponentType(ComponentType.PROCESSOR);
+ instantiatedProcessor.setIdentifier(versionedId);
+ instantiatedProcessor.setProperties(Map.of(renamedPropertyName,
controllerServiceId));
+
+ final FlowDifference propertyRemoved = new StandardFlowDifference(
+ DifferenceType.PROPERTY_REMOVED,
+ versionedProcessor,
+ instantiatedProcessor,
+ legacyPropertyName,
+ controllerServiceId,
+ null,
+ "Legacy property removed");
+
+ final FlowDifference propertyAdded = new StandardFlowDifference(
+ DifferenceType.PROPERTY_ADDED,
+ versionedProcessor,
+ instantiatedProcessor,
+ renamedPropertyName,
+ null,
+ controllerServiceId,
+ "Renamed property added");
+
+ final List<FlowDifference> differences = List.of(propertyRemoved,
propertyAdded);
+
+ final FlowDifferenceFilters.EnvironmentalChangeContext context =
FlowDifferenceFilters.buildEnvironmentalChangeContext(differences, flowManager);
+
+
assertTrue(FlowDifferenceFilters.isEnvironmentalChange(propertyRemoved, null,
flowManager, context));
+ assertTrue(FlowDifferenceFilters.isEnvironmentalChange(propertyAdded,
null, flowManager, context));
+ }
+
@DynamicProperty(name = "Dynamic Property", value = "Value", description =
"Allows dynamic properties")
private static class DynamicAnnotationProcessor extends AbstractProcessor {
@Override