This is an automated email from the ASF dual-hosted git repository.
pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-api.git
The following commit(s) were added to refs/heads/main by this push:
new 8ea4965 NIFI-16253 Added deterministic order for Connector Property
dependencies (#105)
8ea4965 is described below
commit 8ea4965df8391eac4147626d35db2124c22b8fc9
Author: David Handermann <[email protected]>
AuthorDate: Wed Aug 26 10:07:26 2026 -0500
NIFI-16253 Added deterministic order for Connector Property dependencies
(#105)
---
.../components/connector/ConfigurationStep.java | 5 +-
.../connector/ConfigurationStepDependency.java | 4 +-
.../connector/ConnectorPropertyDependency.java | 4 +-
.../connector/ConnectorPropertyDescriptor.java | 6 +-
.../xml/XmlConnectorDocumentationWriter.java | 27 ++++-
.../xml/XmlConnectorDocumentationWriterTest.java | 130 +++++++++++++++++++--
6 files changed, 152 insertions(+), 24 deletions(-)
diff --git
a/src/main/java/org/apache/nifi/components/connector/ConfigurationStep.java
b/src/main/java/org/apache/nifi/components/connector/ConfigurationStep.java
index abbd84d..13c6efc 100644
--- a/src/main/java/org/apache/nifi/components/connector/ConfigurationStep.java
+++ b/src/main/java/org/apache/nifi/components/connector/ConfigurationStep.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -87,7 +88,7 @@ public final class ConfigurationStep {
private String name;
private String description;
private List<ConnectorPropertyGroup> propertyGroups =
Collections.emptyList();
- private final Set<ConfigurationStepDependency> dependencies = new
HashSet<>();
+ private final Set<ConfigurationStepDependency> dependencies = new
LinkedHashSet<>();
public Builder name(final String name) {
this.name = name;
@@ -130,7 +131,7 @@ public final class ConfigurationStep {
} else {
final Set<String> dependentValueSet = dependentValues.stream()
.map(DescribedValue::getValue)
- .collect(Collectors.toSet());
+ .collect(Collectors.toCollection(LinkedHashSet::new));
dependencies.add(new
ConfigurationStepDependency(step.getName(), property.getName(),
dependentValueSet));
}
diff --git
a/src/main/java/org/apache/nifi/components/connector/ConfigurationStepDependency.java
b/src/main/java/org/apache/nifi/components/connector/ConfigurationStepDependency.java
index f4e0ff9..0fa0421 100644
---
a/src/main/java/org/apache/nifi/components/connector/ConfigurationStepDependency.java
+++
b/src/main/java/org/apache/nifi/components/connector/ConfigurationStepDependency.java
@@ -17,7 +17,7 @@
package org.apache.nifi.components.connector;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
@@ -43,7 +43,7 @@ public final class ConfigurationStepDependency {
public ConfigurationStepDependency(final String stepName, final String
propertyName, final Set<String> dependentValues) {
this.stepName = Objects.requireNonNull(stepName, "Step name is
required");
this.propertyName = Objects.requireNonNull(propertyName, "Property
name is required");
- this.dependentValues = dependentValues == null ? null : new
HashSet<>(dependentValues);
+ this.dependentValues = dependentValues == null ? null : new
LinkedHashSet<>(dependentValues);
}
/**
diff --git
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDependency.java
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDependency.java
index d2ecb79..faa26dc 100644
---
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDependency.java
+++
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDependency.java
@@ -17,6 +17,8 @@
package org.apache.nifi.components.connector;
+import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
@@ -26,7 +28,7 @@ public final class ConnectorPropertyDependency {
public ConnectorPropertyDependency(final String propertyName, final
Set<String> dependentValues) {
this.propertyName = propertyName;
- this.dependentValues = Set.copyOf(dependentValues);
+ this.dependentValues = Collections.unmodifiableSet(new
LinkedHashSet<>(dependentValues));
}
public ConnectorPropertyDependency(final String propertyName) {
diff --git
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
index 2072746..2bc8475 100644
---
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
+++
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
@@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -278,7 +278,7 @@ public final class ConnectorPropertyDescriptor {
private List<DescribedValue> allowableValues = null;
private boolean allowableValuesFetchable = false;
private final List<Validator> validators = new ArrayList<>();
- private final Set<ConnectorPropertyDependency> dependencies = new
HashSet<>();
+ private final Set<ConnectorPropertyDependency> dependencies = new
LinkedHashSet<>();
public Builder from(final ConnectorPropertyDescriptor other) {
this.name = other.name;
@@ -438,7 +438,7 @@ public final class ConnectorPropertyDescriptor {
} else {
final Set<String> dependentValueSet = dependentValues.stream()
.map(DescribedValue::getValue)
- .collect(Collectors.toSet());
+ .collect(Collectors.toCollection(LinkedHashSet::new));
dependencies.add(new
ConnectorPropertyDependency(descriptor.getName(), dependentValueSet));
}
diff --git
a/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
b/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
index 86712cd..b9cf150 100644
---
a/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
+++
b/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
@@ -20,9 +20,11 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
import java.util.function.Function;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
@@ -52,6 +54,15 @@ import org.apache.nifi.documentation.ExtensionType;
* removed from one incremental release to the next. Use at your own risk!
*/
public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentationWriter {
+ private static final Comparator<ConfigurationStepDependency>
STEP_DEPENDENCY_COMPARATOR =
+ Comparator.comparing(ConfigurationStepDependency::getStepName)
+ .thenComparing(ConfigurationStepDependency::getPropertyName)
+ .thenComparing(dependency ->
getDependentValuesKey(dependency.getDependentValues()));
+
+ private static final Comparator<ConnectorPropertyDependency>
PROPERTY_DEPENDENCY_COMPARATOR =
+ Comparator.comparing(ConnectorPropertyDependency::getPropertyName)
+ .thenComparing(dependency ->
getDependentValuesKey(dependency.getDependentValues()));
+
private final XMLStreamWriter writer;
public XmlConnectorDocumentationWriter(final OutputStream out) throws
XMLStreamException {
@@ -155,7 +166,7 @@ public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentat
final Set<ConfigurationStepDependency> stepDependencies =
step.getDependencies();
if (stepDependencies != null && !stepDependencies.isEmpty()) {
writeStartElement("stepDependencies");
- for (final ConfigurationStepDependency dependency :
stepDependencies) {
+ for (final ConfigurationStepDependency dependency :
sorted(stepDependencies, STEP_DEPENDENCY_COMPARATOR)) {
writeConfigurationStepDependency(dependency);
}
writeEndElement();
@@ -182,7 +193,7 @@ public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentat
final Set<String> dependentValues = dependency.getDependentValues();
if (dependentValues != null && !dependentValues.isEmpty()) {
- writeTextArray("dependentValues", "dependentValue",
dependentValues);
+ writeTextArray("dependentValues", "dependentValue", new
TreeSet<>(dependentValues));
}
writeEndElement();
@@ -238,7 +249,7 @@ public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentat
final Set<ConnectorPropertyDependency> dependencies =
property.getDependencies();
if (dependencies != null && !dependencies.isEmpty()) {
writeStartElement("dependencies");
- for (final ConnectorPropertyDependency dependency : dependencies) {
+ for (final ConnectorPropertyDependency dependency :
sorted(dependencies, PROPERTY_DEPENDENCY_COMPARATOR)) {
writePropertyDependency(dependency);
}
writeEndElement();
@@ -263,7 +274,7 @@ public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentat
final Set<String> dependentValues = dependency.getDependentValues();
if (dependentValues != null && !dependentValues.isEmpty()) {
- writeTextArray("dependentValues", "dependentValue",
dependentValues);
+ writeTextArray("dependentValues", "dependentValue", new
TreeSet<>(dependentValues));
}
writeEndElement();
@@ -297,6 +308,14 @@ public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentat
writeEndElement();
}
+ private static <T> List<T> sorted(final Collection<T> values, final
Comparator<T> comparator) {
+ return values.stream().sorted(comparator).toList();
+ }
+
+ private static String getDependentValuesKey(final Set<String>
dependentValues) {
+ return dependentValues == null ? "" : String.join(",", new
TreeSet<>(dependentValues));
+ }
+
// Utility methods for XML writing
private void writeStartElement(final String elementName) throws
IOException {
diff --git
a/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
b/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
index 8c2401d..1790ccb 100644
---
a/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
+++
b/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
@@ -47,6 +47,7 @@ import javax.xml.transform.dom.DOMResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathNodes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -55,11 +56,19 @@ import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(MockitoExtension.class)
class XmlConnectorDocumentationWriterTest {
+ private static final String FIRST_DEPENDENT_VALUE = "SASL_PLAINTEXT";
+ private static final String SECOND_DEPENDENT_VALUE = "SASL_SSL";
+ private static final List<String> EXPECTED_DEPENDENT_VALUES =
List.of(FIRST_DEPENDENT_VALUE, SECOND_DEPENDENT_VALUE);
+
+ private static final String FIRST_PROPERTY_NAME = "Alpha";
+ private static final String SECOND_PROPERTY_NAME = "Zulu";
+ private static final String FIRST_STEP_NAME = "Alpha Step";
+ private static final String SECOND_STEP_NAME = "Zulu Step";
+
@Test
void testWriteMinimalConnector() throws Exception {
final Connector connector = new MinimalConnector();
@@ -333,18 +342,32 @@ class XmlConnectorDocumentationWriterTest {
final Node dependentValuesNode = findNode(xpath, document);
assertNotNull(dependentValuesNode);
- final List<String> dependentValues = new ArrayList<>();
- final NodeList valueNodes = dependentValuesNode.getChildNodes();
- for (int i = 0; i < valueNodes.getLength(); i++) {
- final Node valueNode = valueNodes.item(i);
- if ("dependentValue".equals(valueNode.getNodeName())) {
- dependentValues.add(valueNode.getTextContent());
- }
- }
+ assertEquals(List.of("advanced", "expert"), findValues(xpath +
"/dependentValue", document));
+ }
+
+ @Test
+ void testWriteConnectorWithPropertyDependenciesSorted() throws Exception {
+ final Connector connector = new ConnectorWithUnsortedDependencies();
+ final Document document = writeDocumentation(connector);
+
+ final String propertyPath =
"/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup"
+ + "/properties/property[name='Dependent Property']";
+
+ assertEquals(List.of(FIRST_PROPERTY_NAME, SECOND_PROPERTY_NAME),
findValues(propertyPath + "/dependencies/dependency/propertyName", document));
+ assertEquals(EXPECTED_DEPENDENT_VALUES,
+ findValues(propertyPath +
"/dependencies/dependency[propertyName='Zulu']/dependentValues/dependentValue",
document));
+ }
- assertEquals(2, dependentValues.size());
- assertTrue(dependentValues.contains("advanced"));
- assertTrue(dependentValues.contains("expert"));
+ @Test
+ void testWriteConnectorWithStepDependenciesSorted() throws Exception {
+ final Connector connector = new
ConnectorWithUnsortedStepDependencies();
+ final Document document = writeDocumentation(connector);
+
+ final String stepDependenciesPath =
"/extension/configurationSteps/configurationStep[name='Dependent
Step']/stepDependencies";
+
+ assertEquals(List.of(FIRST_STEP_NAME, SECOND_STEP_NAME),
findValues(stepDependenciesPath + "/stepDependency/stepName", document));
+ assertEquals(EXPECTED_DEPENDENT_VALUES,
+ findValues(stepDependenciesPath + "/stepDependency[stepName='Zulu
Step']/dependentValues/dependentValue", document));
}
@Test
@@ -459,6 +482,18 @@ class XmlConnectorDocumentationWriterTest {
return path.evaluateExpression(expression, node, Node.class);
}
+ private List<String> findValues(final String expression, final Node node)
throws XPathExpressionException {
+ final XPathFactory factory = XPathFactory.newInstance();
+ final XPath path = factory.newXPath();
+ final XPathNodes nodes = path.evaluateExpression(expression, node,
XPathNodes.class);
+
+ final List<String> values = new ArrayList<>();
+ for (final Node matchedNode : nodes) {
+ values.add(matchedNode.getTextContent());
+ }
+ return values;
+ }
+
private void assertExtensionNameTypeFound(final Connector connector, final
ExtensionType expectedExtensionType, final Document document) {
assertNotNull(document);
@@ -785,6 +820,77 @@ class XmlConnectorDocumentationWriterTest {
}
}
+ private static class ConnectorWithUnsortedDependencies extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor zuluProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name(SECOND_PROPERTY_NAME)
+ .type(PropertyType.STRING)
+ .allowableValues(EXPECTED_DEPENDENT_VALUES)
+ .build();
+
+ final ConnectorPropertyDescriptor alphaProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name(FIRST_PROPERTY_NAME)
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyDescriptor dependentProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Dependent Property")
+ .type(PropertyType.STRING)
+ .dependsOn(zuluProperty, SECOND_DEPENDENT_VALUE,
FIRST_DEPENDENT_VALUE)
+ .dependsOn(alphaProperty)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Settings")
+ .addProperty(zuluProperty)
+ .addProperty(alphaProperty)
+ .addProperty(dependentProperty)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Settings Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithUnsortedStepDependencies extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor zuluProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Zulu Property")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConfigurationStep zuluStep = new ConfigurationStep.Builder()
+ .name(SECOND_STEP_NAME)
+
.propertyGroups(List.of(ConnectorPropertyGroup.builder().name("Zulu
Group").addProperty(zuluProperty).build()))
+ .build();
+
+ final ConnectorPropertyDescriptor alphaProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Alpha Property")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConfigurationStep alphaStep = new ConfigurationStep.Builder()
+ .name(FIRST_STEP_NAME)
+
.propertyGroups(List.of(ConnectorPropertyGroup.builder().name("Alpha
Group").addProperty(alphaProperty).build()))
+ .build();
+
+ final ConfigurationStep dependentStep = new
ConfigurationStep.Builder()
+ .name("Dependent Step")
+
.propertyGroups(List.of(ConnectorPropertyGroup.builder().name("Dependent
Group").build()))
+ .dependsOn(zuluStep, zuluProperty, SECOND_DEPENDENT_VALUE,
FIRST_DEPENDENT_VALUE)
+ .dependsOn(alphaStep, alphaProperty)
+ .build();
+
+ return List.of(zuluStep, alphaStep, dependentStep);
+ }
+ }
+
private static class ConnectorWithStepDependencyValues extends
MinimalConnector {
@Override
public List<ConfigurationStep> getConfigurationSteps() {