Skip to content

Commit 6033076

Browse files
authored
NIFI-15012 - FlowDifferenceFilters - fix handling of createControllerService() (#10342)
Signed-off-by: Pierre Villard <[email protected]>
1 parent c00b8eb commit 6033076

File tree

2 files changed

+188
-57
lines changed

2 files changed

+188
-57
lines changed

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/FlowDifferenceFilters.java

Lines changed: 138 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import org.apache.nifi.flow.VersionedPort;
4040
import org.apache.nifi.flow.VersionedProcessGroup;
4141
import org.apache.nifi.flow.VersionedProcessor;
42-
import org.apache.nifi.flow.VersionedReportingTask;
42+
import org.apache.nifi.flow.VersionedPropertyDescriptor;
4343
import org.apache.nifi.groups.ProcessGroup;
4444
import org.apache.nifi.processor.Processor;
4545
import org.apache.nifi.processor.Relationship;
@@ -63,7 +63,6 @@
6363
import java.util.Set;
6464
import java.util.function.Predicate;
6565
import java.util.regex.Pattern;
66-
import java.util.stream.Collectors;
6766

6867
public class FlowDifferenceFilters {
6968

@@ -113,19 +112,25 @@ private static boolean isSensitivePropertyDueToGhosting(final FlowDifference dif
113112
return false;
114113
}
115114

116-
final String componentAId = difference.getComponentA().getInstanceIdentifier();
117-
if (componentAId != null) {
118-
final ComponentNode componentNode = getComponent(flowManager, difference.getComponentA().getComponentType(), componentAId);
119-
if (componentNode != null && componentNode.isExtensionMissing()) {
120-
return true;
115+
final VersionedComponent componentA = difference.getComponentA();
116+
if (componentA != null) {
117+
final String componentAId = componentA.getInstanceIdentifier();
118+
if (componentAId != null) {
119+
final ComponentNode componentNode = getComponent(flowManager, componentA.getComponentType(), componentAId);
120+
if (componentNode != null && componentNode.isExtensionMissing()) {
121+
return true;
122+
}
121123
}
122124
}
123125

124-
final String componentBId = difference.getComponentB().getInstanceIdentifier();
125-
if (componentBId != null) {
126-
final ComponentNode componentNode = getComponent(flowManager, difference.getComponentA().getComponentType(), componentBId);
127-
if (componentNode != null && componentNode.isExtensionMissing()) {
128-
return true;
126+
final VersionedComponent componentB = difference.getComponentB();
127+
if (componentB != null) {
128+
final String componentBId = componentB.getInstanceIdentifier();
129+
if (componentBId != null) {
130+
final ComponentNode componentNode = getComponent(flowManager, componentB.getComponentType(), componentBId);
131+
if (componentNode != null && componentNode.isExtensionMissing()) {
132+
return true;
133+
}
129134
}
130135
}
131136

@@ -682,34 +687,27 @@ public static EnvironmentalChangeContext buildEnvironmentalChangeContext(final C
682687
return EnvironmentalChangeContext.empty();
683688
}
684689

685-
final Set<String> serviceIdsReferencedByNewProperties = new HashSet<>();
686690
final Map<String, List<PropertyDiffInfo>> parameterizedAddsByComponent = new HashMap<>();
687691
final Map<String, List<PropertyDiffInfo>> parameterizationRemovalsByComponent = new HashMap<>();
692+
final Map<String, List<PropertyDiffInfo>> controllerServicePropertyAddsByValue = new HashMap<>();
688693
final Map<String, List<PropertyDiffInfo>> propertyAddsByComponent = new HashMap<>();
689694
final Map<String, List<PropertyDiffInfo>> propertyRemovalsByComponent = new HashMap<>();
690695

691696
for (final FlowDifference difference : differences) {
692697
if (difference.getDifferenceType() == DifferenceType.PROPERTY_ADDED) {
693-
final ComponentNode componentNode = Optional.ofNullable(getComponent(flowManager, difference.getComponentB()))
694-
.orElseGet(() -> getComponent(flowManager, difference.getComponentA()));
695-
if (componentNode != null) {
696-
final Optional<String> fieldNameOptional = difference.getFieldName();
697-
if (fieldNameOptional.isPresent()) {
698-
final PropertyDescriptor propertyDescriptor = componentNode.getPropertyDescriptor(fieldNameOptional.get());
699-
if (propertyDescriptor != null && !propertyDescriptor.isDynamic() && propertyDescriptor.getControllerServiceDefinition() != null) {
700-
final Object valueB = difference.getValueB();
701-
if (valueB instanceof String serviceId) {
702-
serviceIdsReferencedByNewProperties.add(serviceId);
703-
}
704-
}
705-
}
706-
}
707-
708698
final Optional<String> componentIdOptional = getComponentInstanceIdentifier(difference);
709699
if (componentIdOptional.isPresent()) {
710700
final PropertyDiffInfo diffInfo = new PropertyDiffInfo(getPropertyValue(difference, false), difference);
711701
propertyAddsByComponent.computeIfAbsent(componentIdOptional.get(), key -> new ArrayList<>()).add(diffInfo);
712702
}
703+
704+
final Optional<String> propertyValue = getPropertyValue(difference, false);
705+
if (propertyValue.isPresent()
706+
&& !isParameterReference(propertyValue.get())
707+
&& isControllerServiceProperty(difference, flowManager)) {
708+
controllerServicePropertyAddsByValue.computeIfAbsent(propertyValue.get(), key -> new ArrayList<>())
709+
.add(new PropertyDiffInfo(propertyValue, difference));
710+
}
713711
}
714712

715713
if (difference.getDifferenceType() == DifferenceType.PROPERTY_REMOVED) {
@@ -743,16 +741,17 @@ public static EnvironmentalChangeContext buildEnvironmentalChangeContext(final C
743741
}
744742
}
745743

746-
final Set<String> serviceIdsWithMatchingAdditions;
747-
if (!serviceIdsReferencedByNewProperties.isEmpty()) {
748-
serviceIdsWithMatchingAdditions = differences.stream()
749-
.filter(diff -> diff.getDifferenceType() == DifferenceType.COMPONENT_ADDED)
750-
.map(FlowDifferenceFilters::extractControllerServiceIdentifier)
751-
.filter(Objects::nonNull)
752-
.filter(serviceIdsReferencedByNewProperties::contains)
753-
.collect(Collectors.toSet());
754-
} else {
755-
serviceIdsWithMatchingAdditions = Collections.emptySet();
744+
final Set<String> serviceIdsWithMatchingAdditions = new HashSet<>();
745+
for (final FlowDifference difference : differences) {
746+
if (difference.getDifferenceType() != DifferenceType.COMPONENT_ADDED) {
747+
continue;
748+
}
749+
750+
for (final String candidateId : getControllerServiceIdentifiers(difference)) {
751+
if (controllerServicePropertyAddsByValue.containsKey(candidateId)) {
752+
serviceIdsWithMatchingAdditions.add(candidateId);
753+
}
754+
}
756755
}
757756

758757
final Set<FlowDifference> parameterizedPropertyRenameDifferences = new HashSet<>();
@@ -851,41 +850,50 @@ private static boolean isControllerServiceCreatedForNewPropertyInternal(final Fl
851850
}
852851

853852
if (difference.getDifferenceType() == DifferenceType.PROPERTY_ADDED) {
854-
final Object valueB = difference.getValueB();
855-
return valueB != null && context.serviceIdsCreatedForNewProperties().contains(valueB);
853+
return getPropertyValue(difference, false)
854+
.filter(value -> context.serviceIdsCreatedForNewProperties().contains(value))
855+
.isPresent();
856856
}
857857

858858
if (difference.getDifferenceType() == DifferenceType.COMPONENT_ADDED) {
859-
final String serviceIdentifier = extractControllerServiceIdentifier(difference);
860-
return serviceIdentifier != null && context.serviceIdsCreatedForNewProperties().contains(serviceIdentifier);
859+
for (final String candidateId : getControllerServiceIdentifiers(difference)) {
860+
if (context.serviceIdsCreatedForNewProperties().contains(candidateId)) {
861+
return true;
862+
}
863+
}
864+
return false;
861865
}
862866

863867
return false;
864868
}
865869

866-
private static String extractControllerServiceIdentifier(final FlowDifference difference) {
867-
final String identifierFromComponentB = extractControllerServiceIdentifier(difference.getComponentB());
868-
if (identifierFromComponentB != null) {
869-
return identifierFromComponentB;
870-
}
870+
private static Set<String> getControllerServiceIdentifiers(final FlowDifference difference) {
871+
final Set<String> identifiers = new HashSet<>();
871872

872-
return extractControllerServiceIdentifier(difference.getComponentA());
873+
addControllerServiceIdentifiers(difference.getComponentB(), identifiers);
874+
addControllerServiceIdentifiers(difference.getComponentA(), identifiers);
875+
876+
return identifiers;
873877
}
874878

875-
private static String extractControllerServiceIdentifier(final VersionedComponent component) {
876-
if (component instanceof InstantiatedVersionedControllerService) {
877-
final InstantiatedVersionedControllerService instantiatedControllerService = (InstantiatedVersionedControllerService) component;
879+
private static void addControllerServiceIdentifiers(final VersionedComponent component, final Set<String> identifiers) {
880+
if (component == null) {
881+
return;
882+
}
883+
884+
if (component instanceof InstantiatedVersionedControllerService instantiatedControllerService) {
878885
final String instanceIdentifier = instantiatedControllerService.getInstanceIdentifier();
879886
if (instanceIdentifier != null) {
880-
return instanceIdentifier;
887+
identifiers.add(instanceIdentifier);
881888
}
882889
}
883890

884891
if (component instanceof VersionedControllerService) {
885-
return component.getIdentifier();
892+
final String identifier = component.getIdentifier();
893+
if (identifier != null) {
894+
identifiers.add(identifier);
895+
}
886896
}
887-
888-
return null;
889897
}
890898

891899
public static boolean isPropertyParameterizationRename(final FlowDifference difference, final EnvironmentalChangeContext context) {
@@ -985,6 +993,59 @@ private static Optional<String> getPropertyValue(final FlowDifference difference
985993
return Optional.empty();
986994
}
987995

996+
private static Optional<PropertyDescriptor> getComponentPropertyDescriptor(final FlowManager flowManager, final FlowDifference difference, final String propertyName) {
997+
final ComponentNode componentNode = Optional.ofNullable(getComponent(flowManager, difference.getComponentB()))
998+
.orElseGet(() -> getComponent(flowManager, difference.getComponentA()));
999+
1000+
if (componentNode == null) {
1001+
return Optional.empty();
1002+
}
1003+
1004+
return Optional.ofNullable(componentNode.getPropertyDescriptor(propertyName));
1005+
}
1006+
1007+
private static Optional<VersionedPropertyDescriptor> getVersionedPropertyDescriptor(final FlowDifference difference, final boolean fromComponentA) {
1008+
final VersionedComponent component = fromComponentA ? difference.getComponentA() : difference.getComponentB();
1009+
final Map<String, VersionedPropertyDescriptor> descriptors = getPropertyDescriptors(component);
1010+
if (descriptors == null || descriptors.isEmpty()) {
1011+
return Optional.empty();
1012+
}
1013+
1014+
final Optional<String> fieldNameOptional = difference.getFieldName();
1015+
if (fieldNameOptional.isEmpty()) {
1016+
return Optional.empty();
1017+
}
1018+
1019+
final String fieldName = fieldNameOptional.get();
1020+
VersionedPropertyDescriptor descriptor = descriptors.get(fieldName);
1021+
if (descriptor == null) {
1022+
descriptor = descriptors.values().stream()
1023+
.filter(candidate -> fieldName.equals(candidate.getName()) || fieldName.equals(candidate.getDisplayName()))
1024+
.findFirst()
1025+
.orElse(null);
1026+
}
1027+
1028+
return Optional.ofNullable(descriptor);
1029+
}
1030+
1031+
private static Map<String, VersionedPropertyDescriptor> getPropertyDescriptors(final VersionedComponent component) {
1032+
final Map<String, VersionedPropertyDescriptor> descriptors;
1033+
1034+
if (component == null) {
1035+
descriptors = Collections.emptyMap();
1036+
} else if (component instanceof VersionedConfigurableComponent configurableComponent) {
1037+
descriptors = configurableComponent.getPropertyDescriptors();
1038+
} else if (component instanceof VersionedProcessor processor) {
1039+
descriptors = processor.getPropertyDescriptors();
1040+
} else if (component instanceof VersionedControllerService controllerService) {
1041+
descriptors = controllerService.getPropertyDescriptors();
1042+
} else {
1043+
descriptors = Collections.emptyMap();
1044+
}
1045+
1046+
return descriptors == null ? Collections.emptyMap() : descriptors;
1047+
}
1048+
9881049
private static Map<String, String> getProperties(final VersionedComponent component) {
9891050
final Map<String, String> properties;
9901051

@@ -996,15 +1057,35 @@ private static Map<String, String> getProperties(final VersionedComponent compon
9961057
properties = processor.getProperties();
9971058
} else if (component instanceof VersionedControllerService controllerService) {
9981059
properties = controllerService.getProperties();
999-
} else if (component instanceof VersionedReportingTask reportingTask) {
1000-
properties = reportingTask.getProperties();
10011060
} else {
10021061
properties = Collections.emptyMap();
10031062
}
10041063

10051064
return properties == null ? Collections.emptyMap() : properties;
10061065
}
10071066

1067+
private static boolean isControllerServiceProperty(final FlowDifference difference, final FlowManager flowManager) {
1068+
final Optional<String> fieldNameOptional = difference.getFieldName();
1069+
if (fieldNameOptional.isEmpty()) {
1070+
return false;
1071+
}
1072+
1073+
final String propertyName = fieldNameOptional.get();
1074+
final Optional<PropertyDescriptor> componentDescriptor = getComponentPropertyDescriptor(flowManager, difference, propertyName);
1075+
if (componentDescriptor.isPresent()) {
1076+
final PropertyDescriptor descriptor = componentDescriptor.get();
1077+
return !descriptor.isDynamic() && descriptor.getControllerServiceDefinition() != null;
1078+
}
1079+
1080+
final Optional<VersionedPropertyDescriptor> versionedDescriptor = getVersionedPropertyDescriptor(difference, false);
1081+
if (versionedDescriptor.isPresent()) {
1082+
final VersionedPropertyDescriptor descriptor = versionedDescriptor.get();
1083+
return Boolean.TRUE.equals(descriptor.getIdentifiesControllerService());
1084+
}
1085+
1086+
return false;
1087+
}
1088+
10081089
private static boolean valuesMatch(final Optional<String> first, final Optional<String> second) {
10091090
return first.isPresent() && second.isPresent() && first.get().equals(second.get());
10101091
}

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/util/TestFlowDifferenceFilters.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.nifi.flow.VersionedControllerService;
3333
import org.apache.nifi.flow.VersionedPort;
3434
import org.apache.nifi.flow.VersionedProcessor;
35+
import org.apache.nifi.flow.VersionedPropertyDescriptor;
3536
import org.apache.nifi.flow.VersionedRemoteGroupPort;
3637
import org.apache.nifi.groups.ProcessGroup;
3738
import org.apache.nifi.processor.AbstractProcessor;
@@ -312,6 +313,55 @@ public void testControllerServiceCreationPairedWithPropertyAdditionIsEnvironment
312313
assertTrue(FlowDifferenceFilters.isEnvironmentalChange(controllerServiceDifference, null, flowManager, context));
313314
}
314315

316+
@Test
317+
public void testControllerServiceCreationEnvironmentalChangeWithoutComponentNode() {
318+
final FlowManager flowManager = Mockito.mock(FlowManager.class);
319+
320+
final String groupId = "group-id";
321+
final String propertyName = "Request Rate Manager";
322+
final String controllerServiceInstanceId = "service-instance-id";
323+
final String controllerServiceVersionedId = "service-versioned-id";
324+
325+
final InstantiatedVersionedControllerService controllerServiceWithNewProperty = new InstantiatedVersionedControllerService("component-instance", groupId);
326+
controllerServiceWithNewProperty.setComponentType(ComponentType.CONTROLLER_SERVICE);
327+
controllerServiceWithNewProperty.setIdentifier(controllerServiceVersionedId);
328+
controllerServiceWithNewProperty.setProperties(Map.of(propertyName, controllerServiceVersionedId));
329+
330+
final VersionedPropertyDescriptor versionedPropertyDescriptor = new VersionedPropertyDescriptor();
331+
versionedPropertyDescriptor.setName(propertyName);
332+
versionedPropertyDescriptor.setDisplayName(propertyName);
333+
versionedPropertyDescriptor.setDynamic(false);
334+
versionedPropertyDescriptor.setIdentifiesControllerService(true);
335+
controllerServiceWithNewProperty.setPropertyDescriptors(Map.of(propertyName, versionedPropertyDescriptor));
336+
337+
final FlowDifference propertyDifference = new StandardFlowDifference(
338+
DifferenceType.PROPERTY_ADDED,
339+
null,
340+
controllerServiceWithNewProperty,
341+
propertyName,
342+
null,
343+
controllerServiceVersionedId,
344+
"Controller service reference added");
345+
346+
final InstantiatedVersionedControllerService instantiatedControllerService = new InstantiatedVersionedControllerService(controllerServiceInstanceId, groupId);
347+
instantiatedControllerService.setComponentType(ComponentType.CONTROLLER_SERVICE);
348+
instantiatedControllerService.setIdentifier(controllerServiceVersionedId);
349+
350+
final FlowDifference controllerServiceDifference = new StandardFlowDifference(
351+
DifferenceType.COMPONENT_ADDED,
352+
null,
353+
instantiatedControllerService,
354+
null,
355+
null,
356+
"Controller service created");
357+
358+
final FlowDifferenceFilters.EnvironmentalChangeContext context = FlowDifferenceFilters.buildEnvironmentalChangeContext(
359+
List.of(propertyDifference, controllerServiceDifference), flowManager);
360+
361+
assertTrue(FlowDifferenceFilters.isEnvironmentalChange(propertyDifference, null, flowManager, context));
362+
assertTrue(FlowDifferenceFilters.isEnvironmentalChange(controllerServiceDifference, null, flowManager, context));
363+
}
364+
315365
@Test
316366
public void testPropertyRenameWithParameterizationObservedAsEnvironmentalChange() {
317367
final FlowManager flowManager = Mockito.mock(FlowManager.class);

0 commit comments

Comments
 (0)