This is an automated email from the ASF dual-hosted git repository.
rfellows pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi-api.git
The following commit(s) were added to refs/heads/NIFI-15258 by this push:
new 4a552ac NIFI-15353: Adding documentation writers for connectors. (#40)
4a552ac is described below
commit 4a552ac0b22ee631770825fc2810536e25be1bcd
Author: Matt Gilman <[email protected]>
AuthorDate: Tue Dec 23 11:50:26 2025 -0500
NIFI-15353: Adding documentation writers for connectors. (#40)
This closes #40
---
.../AbstractConnectorDocumentationWriter.java | 106 +++
.../ConnectorDocumentationWriter.java | 52 ++
.../apache/nifi/documentation/ExtensionType.java | 4 +-
...ocumentationConnectorInitializationContext.java | 54 ++
.../xml/XmlConnectorDocumentationWriter.java | 344 ++++++++
...entationConnectorInitializationContextTest.java | 77 ++
.../xml/XmlConnectorDocumentationWriterTest.java | 866 +++++++++++++++++++++
7 files changed, 1502 insertions(+), 1 deletion(-)
diff --git
a/src/main/java/org/apache/nifi/documentation/AbstractConnectorDocumentationWriter.java
b/src/main/java/org/apache/nifi/documentation/AbstractConnectorDocumentationWriter.java
new file mode 100644
index 0000000..f10fcd1
--- /dev/null
+++
b/src/main/java/org/apache/nifi/documentation/AbstractConnectorDocumentationWriter.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.DeprecationNotice;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.connector.ConfigurationStep;
+import org.apache.nifi.components.connector.Connector;
+import
org.apache.nifi.documentation.init.DocumentationConnectorInitializationContext;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Base class for ConnectorDocumentationWriter that simplifies iterating over
all information for a Connector,
+ * creating a separate method for each piece of documentation, to ensure that
implementations properly override
+ * all methods and therefore properly account for all information about a
Connector.
+ *
+ * Please note that while this class lives within the nifi-api, it is provided
primarily as a means for documentation components within
+ * the NiFi NAR Maven Plugin. Its home is the nifi-api, however, because the
API is needed in order to extract the relevant information and
+ * the NAR Maven Plugin cannot have a direct dependency on nifi-api (doing so
would cause a circular dependency). By having this homed within
+ * the nifi-api, the Maven plugin is able to discover the class dynamically
and invoke the one or two methods necessary to create the documentation.
+ *
+ * This is a new capability for Connectors and therefore, you should
+ * <b>NOTE WELL:</b> At this time, while this class is part of nifi-api, it is
still evolving and may change in a non-backward-compatible manner or even be
+ * removed from one incremental release to the next. Use at your own risk!
+ */
+public abstract class AbstractConnectorDocumentationWriter implements
ConnectorDocumentationWriter {
+
+ @Override
+ public void initialize(final Connector connector) {
+ connector.initialize(new
DocumentationConnectorInitializationContext());
+ }
+
+ @Override
+ public final void write(final Connector connector) throws IOException {
+ writeHeader(connector);
+ writeBody(connector);
+ writeFooter(connector);
+ }
+
+ protected void writeBody(final Connector connector) throws IOException {
+ writeExtensionName(connector.getClass().getName());
+ writeExtensionType(ExtensionType.CONNECTOR);
+
writeDeprecationNotice(connector.getClass().getAnnotation(DeprecationNotice.class));
+ writeDescription(getDescription(connector));
+ writeTags(getTags(connector));
+ writeConfigurationSteps(connector.getConfigurationSteps());
+ writeSeeAlso(connector.getClass().getAnnotation(SeeAlso.class));
+ }
+
+ protected String getDescription(final Connector connector) {
+ final CapabilityDescription capabilityDescription =
connector.getClass().getAnnotation(CapabilityDescription.class);
+ if (capabilityDescription == null) {
+ return null;
+ }
+ return capabilityDescription.value();
+ }
+
+ protected List<String> getTags(final Connector connector) {
+ final Tags tags = connector.getClass().getAnnotation(Tags.class);
+ if (tags == null) {
+ return Collections.emptyList();
+ }
+ final String[] tagValues = tags.value();
+ return tagValues == null ? Collections.emptyList() :
Arrays.asList(tagValues);
+ }
+
+ protected abstract void writeHeader(Connector connector) throws
IOException;
+
+ protected abstract void writeExtensionName(String extensionName) throws
IOException;
+
+ protected abstract void writeExtensionType(ExtensionType extensionType)
throws IOException;
+
+ protected abstract void writeDeprecationNotice(DeprecationNotice
deprecationNotice) throws IOException;
+
+ protected abstract void writeDescription(String description) throws
IOException;
+
+ protected abstract void writeTags(List<String> tags) throws IOException;
+
+ protected abstract void writeConfigurationSteps(List<ConfigurationStep>
configurationSteps) throws IOException;
+
+ protected abstract void writeSeeAlso(SeeAlso seeAlso) throws IOException;
+
+ protected abstract void writeFooter(Connector connector) throws
IOException;
+
+}
+
diff --git
a/src/main/java/org/apache/nifi/documentation/ConnectorDocumentationWriter.java
b/src/main/java/org/apache/nifi/documentation/ConnectorDocumentationWriter.java
new file mode 100644
index 0000000..a7edb41
--- /dev/null
+++
b/src/main/java/org/apache/nifi/documentation/ConnectorDocumentationWriter.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation;
+
+import java.io.IOException;
+import org.apache.nifi.components.connector.Connector;
+
+/**
+ * Generates documentation for an instance of a Connector.
+ *
+ * Please note that while this class lives within the nifi-api, it is provided
primarily as a means for documentation components within
+ * the NiFi NAR Maven Plugin. Its home is the nifi-api, however, because the
API is needed in order to extract the relevant information and
+ * the NAR Maven Plugin cannot have a direct dependency on nifi-api (doing so
would cause a circular dependency). By having this homed within
+ * the nifi-api, the Maven plugin is able to discover the class dynamically
and invoke the one or two methods necessary to create the documentation.
+ *
+ * This is a new capability for Connectors and therefore, you should
+ * <b>NOTE WELL:</b> At this time, while this class is part of nifi-api, it is
still evolving and may change in a non-backward-compatible manner or even be
+ * removed from one incremental release to the next. Use at your own risk!
+ */
+public interface ConnectorDocumentationWriter {
+
+ /**
+ * Calls initialize on the connector. Must be called before calling any
write methods.
+ *
+ * @param connector the connector to initialize
+ */
+ void initialize(Connector connector);
+
+ /**
+ * Write the documentation for the given connector.
+ *
+ * @param connector the connector to document
+ * @throws IOException if an error occurs writing the documentation
+ */
+ void write(Connector connector) throws IOException;
+
+}
+
diff --git a/src/main/java/org/apache/nifi/documentation/ExtensionType.java
b/src/main/java/org/apache/nifi/documentation/ExtensionType.java
index 7f6fbab..160966e 100644
--- a/src/main/java/org/apache/nifi/documentation/ExtensionType.java
+++ b/src/main/java/org/apache/nifi/documentation/ExtensionType.java
@@ -27,5 +27,7 @@ public enum ExtensionType {
PARAMETER_PROVIDER,
- FLOW_REGISTRY_CLIENT;
+ FLOW_REGISTRY_CLIENT,
+
+ CONNECTOR;
}
diff --git
a/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
b/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
new file mode 100644
index 0000000..3529a9f
--- /dev/null
+++
b/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation.init;
+
+import java.util.UUID;
+import org.apache.nifi.components.connector.ConnectorInitializationContext;
+import org.apache.nifi.components.connector.FlowUpdateException;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.flow.VersionedExternalFlow;
+import org.apache.nifi.logging.ComponentLog;
+
+/**
+ * A ConnectorInitializationContext implementation for use during
documentation generation.
+ * This context provides minimal functionality needed to initialize a
Connector for the purposes
+ * of extracting its configuration steps and other documentation metadata.
+ */
+public class DocumentationConnectorInitializationContext implements
ConnectorInitializationContext {
+ private final String uuid = UUID.randomUUID().toString();
+
+ @Override
+ public String getIdentifier() {
+ return uuid;
+ }
+
+ @Override
+ public String getName() {
+ return "DocumentationConnector";
+ }
+
+ @Override
+ public ComponentLog getLogger() {
+ return new NopComponentLog();
+ }
+
+ @Override
+ public void updateFlow(final FlowContext flowContext, final
VersionedExternalFlow versionedExternalFlow) throws FlowUpdateException {
+ // No-op for documentation purposes - we don't actually update any
flows
+ }
+}
+
diff --git
a/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
b/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
new file mode 100644
index 0000000..9fdcc19
--- /dev/null
+++
b/src/main/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriter.java
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation.xml;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.apache.nifi.annotation.documentation.DeprecationNotice;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.components.DescribedValue;
+import org.apache.nifi.components.connector.ConfigurationStep;
+import org.apache.nifi.components.connector.ConfigurationStepDependency;
+import org.apache.nifi.components.connector.Connector;
+import org.apache.nifi.components.connector.ConnectorPropertyDependency;
+import org.apache.nifi.components.connector.ConnectorPropertyDescriptor;
+import org.apache.nifi.components.connector.ConnectorPropertyGroup;
+import org.apache.nifi.documentation.AbstractConnectorDocumentationWriter;
+import org.apache.nifi.documentation.ExtensionType;
+
+/**
+ * XML-based implementation of ConnectorDocumentationWriter.
+ * Please note that while this class lives within the nifi-api, it is provided
primarily as a means for documentation components within
+ * the NiFi NAR Maven Plugin. Its home is the nifi-api, however, because the
API is needed in order to extract the relevant information and
+ * the NAR Maven Plugin cannot have a direct dependency on nifi-api (doing so
would cause a circular dependency). By having this homed within
+ * the nifi-api, the Maven plugin is able to discover the class dynamically
and invoke the one or two methods necessary to create the documentation.
+ *
+ * This is a new capability for Connectors and therefore, you should
+ * <b>NOTE WELL:</b> At this time, while this class is part of nifi-api, it is
still evolving and may change in a non-backward-compatible manner or even be
+ * removed from one incremental release to the next. Use at your own risk!
+ */
+public class XmlConnectorDocumentationWriter extends
AbstractConnectorDocumentationWriter {
+ private final XMLStreamWriter writer;
+
+ public XmlConnectorDocumentationWriter(final OutputStream out) throws
XMLStreamException {
+ this.writer =
XMLOutputFactory.newInstance().createXMLStreamWriter(out, "UTF-8");
+ }
+
+ public XmlConnectorDocumentationWriter(final XMLStreamWriter writer) {
+ this.writer = writer;
+ }
+
+ @Override
+ protected void writeHeader(final Connector connector) throws IOException {
+ writeStartElement("extension");
+ }
+
+ @Override
+ protected void writeExtensionName(final String extensionName) throws
IOException {
+ writeTextElement("name", extensionName);
+ }
+
+ @Override
+ protected void writeExtensionType(final ExtensionType extensionType)
throws IOException {
+ writeTextElement("type", extensionType.name());
+ }
+
+ @Override
+ protected void writeDeprecationNotice(final DeprecationNotice
deprecationNotice) throws IOException {
+ if (deprecationNotice == null) {
+ return;
+ }
+
+ final Class<?>[] classes = deprecationNotice.alternatives();
+ final String[] classNames = deprecationNotice.classNames();
+
+ final Set<String> alternatives = new LinkedHashSet<>();
+ for (final Class<?> alternativeClass : classes) {
+ alternatives.add(alternativeClass.getName());
+ }
+
+ Collections.addAll(alternatives, classNames);
+
+ writeStartElement("deprecationNotice");
+ writeTextElement("reason", deprecationNotice.reason());
+ writeTextArray("alternatives", "alternative", alternatives);
+ writeEndElement();
+ }
+
+ @Override
+ protected void writeDescription(final String description) throws
IOException {
+ if (description == null) {
+ return;
+ }
+ writeTextElement("description", description);
+ }
+
+ @Override
+ protected void writeTags(final List<String> tags) throws IOException {
+ if (tags == null || tags.isEmpty()) {
+ return;
+ }
+ writeTextArray("tags", "tag", tags);
+ }
+
+ @Override
+ protected void writeConfigurationSteps(final List<ConfigurationStep>
configurationSteps) throws IOException {
+ if (configurationSteps == null || configurationSteps.isEmpty()) {
+ return;
+ }
+
+ writeStartElement("configurationSteps");
+ for (final ConfigurationStep step : configurationSteps) {
+ writeConfigurationStep(step);
+ }
+ writeEndElement();
+ }
+
+ private void writeConfigurationStep(final ConfigurationStep step) throws
IOException {
+ writeStartElement("configurationStep");
+
+ writeTextElement("name", step.getName());
+ if (step.getDescription() != null) {
+ writeTextElement("description", step.getDescription());
+ }
+
+ // Write step dependencies
+ final Set<ConfigurationStepDependency> stepDependencies =
step.getDependencies();
+ if (stepDependencies != null && !stepDependencies.isEmpty()) {
+ writeStartElement("stepDependencies");
+ for (final ConfigurationStepDependency dependency :
stepDependencies) {
+ writeConfigurationStepDependency(dependency);
+ }
+ writeEndElement();
+ }
+
+ // Write property groups
+ final List<ConnectorPropertyGroup> propertyGroups =
step.getPropertyGroups();
+ if (propertyGroups != null && !propertyGroups.isEmpty()) {
+ writeStartElement("propertyGroups");
+ for (final ConnectorPropertyGroup group : propertyGroups) {
+ writePropertyGroup(group);
+ }
+ writeEndElement();
+ }
+
+ writeEndElement();
+ }
+
+ private void writeConfigurationStepDependency(final
ConfigurationStepDependency dependency) throws IOException {
+ writeStartElement("stepDependency");
+
+ writeTextElement("stepName", dependency.getStepName());
+ writeTextElement("propertyName", dependency.getPropertyName());
+
+ final Set<String> dependentValues = dependency.getDependentValues();
+ if (dependentValues != null && !dependentValues.isEmpty()) {
+ writeTextArray("dependentValues", "dependentValue",
dependentValues);
+ }
+
+ writeEndElement();
+ }
+
+ private void writePropertyGroup(final ConnectorPropertyGroup group) throws
IOException {
+ writeStartElement("propertyGroup");
+
+ if (group.getName() != null) {
+ writeTextElement("name", group.getName());
+ }
+ if (group.getDescription() != null) {
+ writeTextElement("description", group.getDescription());
+ }
+
+ final List<ConnectorPropertyDescriptor> properties =
group.getProperties();
+ if (properties != null && !properties.isEmpty()) {
+ writeStartElement("properties");
+ for (final ConnectorPropertyDescriptor property : properties) {
+ writeConnectorProperty(property);
+ }
+ writeEndElement();
+ }
+
+ writeEndElement();
+ }
+
+ private void writeConnectorProperty(final ConnectorPropertyDescriptor
property) throws IOException {
+ writeStartElement("property");
+
+ writeTextElement("name", property.getName());
+ if (property.getDescription() != null) {
+ writeTextElement("description", property.getDescription());
+ }
+ if (property.getDefaultValue() != null) {
+ writeTextElement("defaultValue", property.getDefaultValue());
+ }
+ writeBooleanElement("required", property.isRequired());
+ writeTextElement("propertyType", property.getType().name());
+ writeBooleanElement("allowableValuesFetchable",
property.isAllowableValuesFetchable());
+
+ // Write allowable values
+ final List<DescribedValue> allowableValues =
property.getAllowableValues();
+ if (allowableValues != null && !allowableValues.isEmpty()) {
+ writeStartElement("allowableValues");
+ for (final DescribedValue value : allowableValues) {
+ writeAllowableValue(value);
+ }
+ writeEndElement();
+ }
+
+ // Write property dependencies
+ final Set<ConnectorPropertyDependency> dependencies =
property.getDependencies();
+ if (dependencies != null && !dependencies.isEmpty()) {
+ writeStartElement("dependencies");
+ for (final ConnectorPropertyDependency dependency : dependencies) {
+ writePropertyDependency(dependency);
+ }
+ writeEndElement();
+ }
+
+ writeEndElement();
+ }
+
+ private void writeAllowableValue(final DescribedValue value) throws
IOException {
+ writeStartElement("allowableValue");
+ writeTextElement("displayName", value.getDisplayName());
+ writeTextElement("value", value.getValue());
+ if (value.getDescription() != null) {
+ writeTextElement("description", value.getDescription());
+ }
+ writeEndElement();
+ }
+
+ private void writePropertyDependency(final ConnectorPropertyDependency
dependency) throws IOException {
+ writeStartElement("dependency");
+ writeTextElement("propertyName", dependency.getPropertyName());
+
+ final Set<String> dependentValues = dependency.getDependentValues();
+ if (dependentValues != null && !dependentValues.isEmpty()) {
+ writeTextArray("dependentValues", "dependentValue",
dependentValues);
+ }
+
+ writeEndElement();
+ }
+
+ @Override
+ protected void writeSeeAlso(final SeeAlso seeAlso) throws IOException {
+ if (seeAlso == null) {
+ return;
+ }
+
+ final Class<?>[] classes = seeAlso.value();
+ final String[] classNames = seeAlso.classNames();
+
+ final Set<String> toSee = new LinkedHashSet<>();
+ if (classes != null) {
+ for (final Class<?> classToSee : classes) {
+ toSee.add(classToSee.getName());
+ }
+ }
+
+ if (classNames != null) {
+ Collections.addAll(toSee, classNames);
+ }
+
+ writeTextArray("seeAlso", "see", toSee);
+ }
+
+ @Override
+ protected void writeFooter(final Connector connector) throws IOException {
+ writeEndElement();
+ }
+
+ // Utility methods for XML writing
+
+ private void writeStartElement(final String elementName) throws
IOException {
+ try {
+ writer.writeStartElement(elementName);
+ } catch (final XMLStreamException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeEndElement() throws IOException {
+ try {
+ writer.writeEndElement();
+ } catch (final XMLStreamException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeTextElement(final String name, final String text) throws
IOException {
+ writeStartElement(name);
+ writeText(text);
+ writeEndElement();
+ }
+
+ private void writeBooleanElement(final String name, final boolean value)
throws IOException {
+ writeTextElement(name, String.valueOf(value));
+ }
+
+ private void writeText(final String text) throws IOException {
+ if (text == null) {
+ return;
+ }
+ try {
+ writer.writeCharacters(text);
+ } catch (final XMLStreamException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeTextArray(final String outerTagName, final String
elementTagName, final Collection<String> values) throws IOException {
+ writeTextArray(outerTagName, elementTagName, values, String::toString);
+ }
+
+ private <T> void writeTextArray(final String outerTagName, final String
elementTagName, final Collection<T> values, final Function<T, String>
transform) throws IOException {
+ writeStartElement(outerTagName);
+
+ if (values != null) {
+ for (final T value : values) {
+ writeStartElement(elementTagName);
+ if (value != null) {
+ writeText(transform.apply(value));
+ }
+ writeEndElement();
+ }
+ }
+
+ writeEndElement();
+ }
+
+}
+
diff --git
a/src/test/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContextTest.java
b/src/test/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContextTest.java
new file mode 100644
index 0000000..96c4dcb
--- /dev/null
+++
b/src/test/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContextTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation.init;
+
+import org.apache.nifi.logging.ComponentLog;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class DocumentationConnectorInitializationContextTest {
+
+ private DocumentationConnectorInitializationContext context;
+
+ @BeforeEach
+ void setUp() {
+ context = new DocumentationConnectorInitializationContext();
+ }
+
+ @Test
+ void testGetIdentifierReturnsNonNullUUID() {
+ final String identifier = context.getIdentifier();
+
+ assertNotNull(identifier);
+ }
+
+ @Test
+ void testGetIdentifierReturnsConsistentValue() {
+ final String identifier1 = context.getIdentifier();
+ final String identifier2 = context.getIdentifier();
+
+ assertEquals(identifier1, identifier2);
+ }
+
+ @Test
+ void testDifferentInstancesHaveDifferentIdentifiers() {
+ final DocumentationConnectorInitializationContext otherContext = new
DocumentationConnectorInitializationContext();
+
+ assertNotEquals(context.getIdentifier(), otherContext.getIdentifier());
+ }
+
+ @Test
+ void testGetNameReturnsExpectedValue() {
+ assertEquals("DocumentationConnector", context.getName());
+ }
+
+ @Test
+ void testGetLoggerReturnsNopComponentLog() {
+ final ComponentLog logger = context.getLogger();
+
+ assertNotNull(logger);
+ assertEquals(NopComponentLog.class, logger.getClass());
+ }
+
+ @Test
+ void testUpdateFlowDoesNotThrow() throws Exception {
+ // updateFlow is a no-op for documentation purposes, should not throw
+ context.updateFlow(null, null);
+ }
+}
+
diff --git
a/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
b/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
new file mode 100644
index 0000000..dc5912d
--- /dev/null
+++
b/src/test/java/org/apache/nifi/documentation/xml/XmlConnectorDocumentationWriterTest.java
@@ -0,0 +1,866 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.documentation.xml;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.DeprecationNotice;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.connector.AbstractConnector;
+import org.apache.nifi.components.connector.ConfigurationStep;
+import org.apache.nifi.components.connector.Connector;
+import org.apache.nifi.components.connector.ConnectorPropertyDescriptor;
+import org.apache.nifi.components.connector.ConnectorPropertyGroup;
+import org.apache.nifi.components.connector.PropertyType;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.documentation.ExtensionType;
+import org.apache.nifi.flow.VersionedExternalFlow;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+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 {
+
+ @Test
+ void testWriteMinimalConnector() throws Exception {
+ final Connector connector = new MinimalConnector();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+ }
+
+ @Test
+ void testWriteConnectorWithDescription() throws Exception {
+ final Connector connector = new DescribedConnector();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node descriptionNode = findNode("/extension/description",
document);
+ assertNotNull(descriptionNode);
+ assertEquals("A connector with a description",
descriptionNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithTags() throws Exception {
+ final Connector connector = new TaggedConnector();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node tagsNode = findNode("/extension/tags", document);
+ assertNotNull(tagsNode);
+
+ final List<String> tagValues = new ArrayList<>();
+ final NodeList tagNodes = tagsNode.getChildNodes();
+ for (int i = 0; i < tagNodes.getLength(); i++) {
+ final Node tagNode = tagNodes.item(i);
+ assertEquals("tag", tagNode.getNodeName());
+ tagValues.add(tagNode.getTextContent());
+ }
+
+ assertEquals(List.of("test", "documentation", "connector"), tagValues);
+ }
+
+ @Test
+ void testWriteDeprecatedConnector() throws Exception {
+ final Connector connector = new DeprecatedConnector();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node deprecationNoticeNode =
findNode("/extension/deprecationNotice", document);
+ assertNotNull(deprecationNoticeNode);
+
+ final Node reasonNode =
findNode("/extension/deprecationNotice/reason", document);
+ assertNotNull(reasonNode);
+ assertEquals("Use a different connector", reasonNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithSeeAlso() throws Exception {
+ final Connector connector = new SeeAlsoConnector();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node seeAlsoNode = findNode("/extension/seeAlso", document);
+ assertNotNull(seeAlsoNode);
+
+ final List<String> seeAlsoValues = new ArrayList<>();
+ final NodeList seeNodes = seeAlsoNode.getChildNodes();
+ for (int i = 0; i < seeNodes.getLength(); i++) {
+ final Node seeNode = seeNodes.item(i);
+ assertEquals("see", seeNode.getNodeName());
+ seeAlsoValues.add(seeNode.getTextContent());
+ }
+
+ assertEquals(1, seeAlsoValues.size());
+
assertEquals("org.apache.nifi.documentation.xml.XmlConnectorDocumentationWriterTest$MinimalConnector",
seeAlsoValues.getFirst());
+ }
+
+ @Test
+ void testWriteConnectorWithConfigurationSteps() throws Exception {
+ final Connector connector = new ConnectorWithConfigurationSteps();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node configStepsNode = findNode("/extension/configurationSteps",
document);
+ assertNotNull(configStepsNode);
+
+ final Node firstStepNode =
findNode("/extension/configurationSteps/configurationStep[name='Step One']",
document);
+ assertNotNull(firstStepNode);
+
+ final Node stepNameNode =
findNode("/extension/configurationSteps/configurationStep/name", document);
+ assertNotNull(stepNameNode);
+ assertEquals("Step One", stepNameNode.getTextContent());
+
+ final Node stepDescriptionNode =
findNode("/extension/configurationSteps/configurationStep/description",
document);
+ assertNotNull(stepDescriptionNode);
+ assertEquals("First configuration step",
stepDescriptionNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithConfigurationStepWithoutDescription() throws
Exception {
+ final Connector connector = new ConnectorWithStepWithoutDescription();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node stepNameNode =
findNode("/extension/configurationSteps/configurationStep/name", document);
+ assertNotNull(stepNameNode);
+ assertEquals("Minimal Step", stepNameNode.getTextContent());
+
+ // Step description is optional - should not be present when not
provided
+ final Node stepDescriptionNode =
findNode("/extension/configurationSteps/configurationStep/description",
document);
+ assertNull(stepDescriptionNode);
+ }
+
+ @Test
+ void testWriteConnectorWithPropertyGroups() throws Exception {
+ final Connector connector = new ConnectorWithPropertyGroups();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node propertyGroupsNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups",
document);
+ assertNotNull(propertyGroupsNode);
+
+ final Node propertyGroupNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup",
document);
+ assertNotNull(propertyGroupNode);
+
+ final Node groupNameNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/name",
document);
+ assertNotNull(groupNameNode);
+ assertEquals("Connection Settings", groupNameNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithProperties() throws Exception {
+ final Connector connector = new ConnectorWithProperties();
+ final Document document = writeDocumentation(connector);
+
+ assertExtensionNameTypeFound(connector, ExtensionType.CONNECTOR,
document);
+
+ final Node propertiesNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties",
document);
+ assertNotNull(propertiesNode);
+
+ final Node propertyNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property",
document);
+ assertNotNull(propertyNode);
+
+ final Node propertyNameNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/name",
document);
+ assertNotNull(propertyNameNode);
+ assertEquals("Hostname", propertyNameNode.getTextContent());
+
+ final Node propertyDescriptionNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/description",
document);
+ assertNotNull(propertyDescriptionNode);
+ assertEquals("The hostname to connect to",
propertyDescriptionNode.getTextContent());
+
+ final Node propertyRequiredNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/required",
document);
+ assertNotNull(propertyRequiredNode);
+ assertEquals("true", propertyRequiredNode.getTextContent());
+
+ final Node propertyTypeNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/propertyType",
document);
+ assertNotNull(propertyTypeNode);
+ assertEquals("STRING", propertyTypeNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithPropertyDefaultValue() throws Exception {
+ final Connector connector = new ConnectorWithPropertyDefaultValue();
+ final Document document = writeDocumentation(connector);
+
+ final Node defaultValueNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/defaultValue",
document);
+ assertNotNull(defaultValueNode);
+ assertEquals("localhost", defaultValueNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithAllowableValues() throws Exception {
+ final Connector connector = new ConnectorWithAllowableValues();
+ final Document document = writeDocumentation(connector);
+
+ final Node allowableValuesNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/allowableValues",
document);
+ assertNotNull(allowableValuesNode);
+
+ final List<String> allowableValues = new ArrayList<>();
+ final NodeList valueNodes = allowableValuesNode.getChildNodes();
+ for (int i = 0; i < valueNodes.getLength(); i++) {
+ final Node valueNode = valueNodes.item(i);
+ if ("allowableValue".equals(valueNode.getNodeName())) {
+ final Node valueTextNode = findNode("value", valueNode);
+ if (valueTextNode != null) {
+ allowableValues.add(valueTextNode.getTextContent());
+ }
+ }
+ }
+
+ assertEquals(List.of("TCP", "UDP"), allowableValues);
+ }
+
+ @Test
+ void testWriteConnectorWithPropertyDependencies() throws Exception {
+ final Connector connector = new ConnectorWithPropertyDependencies();
+ final Document document = writeDocumentation(connector);
+
+ final Node dependenciesNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property[name='Port']/dependencies",
document);
+ assertNotNull(dependenciesNode);
+
+ final Node dependencyNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property[name='Port']/dependencies/dependency",
document);
+ assertNotNull(dependencyNode);
+
+ final Node dependencyPropertyNameNode = findNode(
+
"/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property[name='Port']/dependencies/dependency/propertyName",
+ document);
+ assertNotNull(dependencyPropertyNameNode);
+ assertEquals("Hostname", dependencyPropertyNameNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithStepDependencies() throws Exception {
+ final Connector connector = new ConnectorWithStepDependencies();
+ final Document document = writeDocumentation(connector);
+
+ final Node stepDependenciesNode =
findNode("/extension/configurationSteps/configurationStep[name='Step
Two']/stepDependencies", document);
+ assertNotNull(stepDependenciesNode);
+
+ final Node stepDependencyNode =
findNode("/extension/configurationSteps/configurationStep[name='Step
Two']/stepDependencies/stepDependency", document);
+ assertNotNull(stepDependencyNode);
+
+ final Node stepNameNode =
findNode("/extension/configurationSteps/configurationStep[name='Step
Two']/stepDependencies/stepDependency/stepName", document);
+ assertNotNull(stepNameNode);
+ assertEquals("Step One", stepNameNode.getTextContent());
+
+ final Node propertyNameNode =
findNode("/extension/configurationSteps/configurationStep[name='Step
Two']/stepDependencies/stepDependency/propertyName", document);
+ assertNotNull(propertyNameNode);
+ assertEquals("Enable Advanced", propertyNameNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithFetchableAllowableValues() throws Exception {
+ final Connector connector = new
ConnectorWithFetchableAllowableValues();
+ final Document document = writeDocumentation(connector);
+
+ final Node allowableValuesFetchableNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/allowableValuesFetchable",
document);
+ assertNotNull(allowableValuesFetchableNode);
+ assertEquals("true", allowableValuesFetchableNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithNoDescription() throws Exception {
+ final Connector connector = new MinimalConnector();
+ final Document document = writeDocumentation(connector);
+
+ final Node descriptionNode = findNode("/extension/description",
document);
+ assertNull(descriptionNode);
+ }
+
+ @Test
+ void testWriteConnectorWithNoTags() throws Exception {
+ final Connector connector = new MinimalConnector();
+ final Document document = writeDocumentation(connector);
+
+ final Node tagsNode = findNode("/extension/tags", document);
+ assertNull(tagsNode);
+ }
+
+ @Test
+ void testWriteConnectorWithPropertyDependencyValues() throws Exception {
+ final Connector connector = new
ConnectorWithPropertyDependencyValues();
+ final Document document = writeDocumentation(connector);
+
+ final String xpath =
"/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup"
+ + "/properties/property[name='Advanced
Setting']/dependencies/dependency/dependentValues";
+ 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(2, dependentValues.size());
+ assertTrue(dependentValues.contains("advanced"));
+ assertTrue(dependentValues.contains("expert"));
+ }
+
+ @Test
+ void testWriteConnectorWithStepDependencyValues() throws Exception {
+ final Connector connector = new ConnectorWithStepDependencyValues();
+ final Document document = writeDocumentation(connector);
+
+ final Node dependentValuesNode =
findNode("/extension/configurationSteps/configurationStep[name='Step
Two']/stepDependencies/stepDependency/dependentValues", 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(1, dependentValues.size());
+ assertEquals("true", dependentValues.getFirst());
+ }
+
+ @Test
+ void testWriteConnectorWithMultipleConfigurationSteps() throws Exception {
+ final Connector connector = new ConnectorWithMultipleSteps();
+ final Document document = writeDocumentation(connector);
+
+ final Node configStepsNode = findNode("/extension/configurationSteps",
document);
+ assertNotNull(configStepsNode);
+
+ final List<String> stepNames = new ArrayList<>();
+ final NodeList stepNodes = configStepsNode.getChildNodes();
+ for (int i = 0; i < stepNodes.getLength(); i++) {
+ final Node stepNode = stepNodes.item(i);
+ if ("configurationStep".equals(stepNode.getNodeName())) {
+ final Node nameNode = findNode("name", stepNode);
+ if (nameNode != null) {
+ stepNames.add(nameNode.getTextContent());
+ }
+ }
+ }
+
+ assertEquals(List.of("Connection", "Authentication", "Advanced"),
stepNames);
+ }
+
+ @Test
+ void testWriteConnectorWithIntegerPropertyType() throws Exception {
+ final Connector connector = new ConnectorWithIntegerProperty();
+ final Document document = writeDocumentation(connector);
+
+ final Node propertyTypeNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/propertyType",
document);
+ assertNotNull(propertyTypeNode);
+ assertEquals("INTEGER", propertyTypeNode.getTextContent());
+ }
+
+ @Test
+ void testWriteConnectorWithBooleanPropertyType() throws Exception {
+ final Connector connector = new ConnectorWithBooleanProperty();
+ final Document document = writeDocumentation(connector);
+
+ final Node propertyTypeNode =
findNode("/extension/configurationSteps/configurationStep/propertyGroups/propertyGroup/properties/property/propertyType",
document);
+ assertNotNull(propertyTypeNode);
+ assertEquals("BOOLEAN", propertyTypeNode.getTextContent());
+ }
+
+ private Node findNode(final String expression, final Node node) throws
XPathExpressionException {
+ final XPathFactory factory = XPathFactory.newInstance();
+ final XPath path = factory.newXPath();
+
+ return path.evaluateExpression(expression, node, Node.class);
+ }
+
+ private void assertExtensionNameTypeFound(final Connector connector, final
ExtensionType expectedExtensionType, final Document document) {
+ assertNotNull(document);
+
+ final Node extensionNode = document.getFirstChild();
+ assertEquals("extension", extensionNode.getNodeName());
+
+ final Node nameNode = extensionNode.getFirstChild();
+ assertEquals("name", nameNode.getNodeName());
+ assertEquals(connector.getClass().getName(),
nameNode.getTextContent());
+
+ final Node typeNode = nameNode.getNextSibling();
+ assertEquals("type", typeNode.getNodeName());
+ assertEquals(expectedExtensionType.name(), typeNode.getTextContent());
+ }
+
+ private Document writeDocumentation(final Connector connector) throws
Exception {
+ final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
+ final DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
+ final DocumentBuilder documentBuilder =
documentBuilderFactory.newDocumentBuilder();
+ final Document document = documentBuilder.newDocument();
+
+ final DOMResult result = new DOMResult(document);
+ final XMLStreamWriter streamWriter =
outputFactory.createXMLStreamWriter(result);
+
+ final XmlConnectorDocumentationWriter documentationWriter = new
XmlConnectorDocumentationWriter(streamWriter);
+
+ try {
+ documentationWriter.initialize(connector);
+ documentationWriter.write(connector);
+ } finally {
+ streamWriter.close();
+ }
+
+ return document;
+ }
+
+ // Test Connector implementations
+
+ private static class MinimalConnector extends AbstractConnector {
+ @Override
+ public VersionedExternalFlow getInitialFlow() {
+ return null;
+ }
+
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ protected void onStepConfigured(final String stepName, final
FlowContext workingContext) {
+ }
+
+ @Override
+ public void applyUpdate(final FlowContext workingFlowContext, final
FlowContext activeFlowContext) {
+ }
+
+ @Override
+ public List<ConfigVerificationResult> verifyConfigurationStep(final
String stepName, final Map<String, String> overrides, final FlowContext
flowContext) {
+ return Collections.emptyList();
+ }
+ }
+
+ @CapabilityDescription("A connector with a description")
+ private static class DescribedConnector extends MinimalConnector {
+ }
+
+ @Tags({"test", "documentation", "connector"})
+ private static class TaggedConnector extends MinimalConnector {
+ }
+
+ @DeprecationNotice(reason = "Use a different connector")
+ private static class DeprecatedConnector extends MinimalConnector {
+ }
+
+ @SeeAlso(classNames =
"org.apache.nifi.documentation.xml.XmlConnectorDocumentationWriterTest$MinimalConnector")
+ private static class SeeAlsoConnector extends MinimalConnector {
+ }
+
+ private static class ConnectorWithConfigurationSteps extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Step One")
+ .description("First configuration step")
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithStepWithoutDescription extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Minimal Step")
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithPropertyGroups extends MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Connection Settings")
+ .description("Settings for connection")
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithProperties extends MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Hostname")
+ .description("The hostname to connect to")
+ .required(true)
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Connection Settings")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithPropertyDefaultValue extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Hostname")
+ .description("The hostname to connect to")
+ .defaultValue("localhost")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Connection Settings")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithAllowableValues extends MinimalConnector
{
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Protocol")
+ .description("The protocol to use")
+ .allowableValues(
+ new AllowableValue("TCP", "TCP", "Transmission Control
Protocol"),
+ new AllowableValue("UDP", "UDP", "User Datagram Protocol")
+ )
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Protocol Settings")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Protocol Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithPropertyDependencies extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor hostnameProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Hostname")
+ .description("The hostname to connect to")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyDescriptor portProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Port")
+ .description("The port to connect to")
+ .type(PropertyType.INTEGER)
+ .dependsOn(hostnameProperty)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Connection Settings")
+ .addProperty(hostnameProperty)
+ .addProperty(portProperty)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithStepDependencies extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor enableAdvanced = new
ConnectorPropertyDescriptor.Builder()
+ .name("Enable Advanced")
+ .description("Enable advanced settings")
+ .type(PropertyType.BOOLEAN)
+ .build();
+
+ final ConnectorPropertyGroup group1 =
ConnectorPropertyGroup.builder()
+ .name("Basic Settings")
+ .addProperty(enableAdvanced)
+ .build();
+
+ final ConfigurationStep step1 = new ConfigurationStep.Builder()
+ .name("Step One")
+ .propertyGroups(List.of(group1))
+ .build();
+
+ final ConnectorPropertyDescriptor advancedProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Advanced Setting")
+ .description("An advanced setting")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group2 =
ConnectorPropertyGroup.builder()
+ .name("Advanced Settings")
+ .addProperty(advancedProperty)
+ .build();
+
+ final ConfigurationStep step2 = new ConfigurationStep.Builder()
+ .name("Step Two")
+ .propertyGroups(List.of(group2))
+ .dependsOn(step1, enableAdvanced, "true")
+ .build();
+
+ return List.of(step1, step2);
+ }
+ }
+
+ private static class ConnectorWithFetchableAllowableValues extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Dynamic Options")
+ .description("Options that are fetched dynamically")
+ .allowableValuesFetchable(true)
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Dynamic Settings")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Dynamic Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithPropertyDependencyValues extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor modeProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Mode")
+ .description("The mode of operation")
+ .type(PropertyType.STRING)
+ .allowableValues("basic", "advanced", "expert")
+ .build();
+
+ final ConnectorPropertyDescriptor advancedProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Advanced Setting")
+ .description("An advanced setting")
+ .type(PropertyType.STRING)
+ .dependsOn(modeProperty, "advanced", "expert")
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Settings")
+ .addProperty(modeProperty)
+ .addProperty(advancedProperty)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Settings Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithStepDependencyValues extends
MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor enableAdvanced = new
ConnectorPropertyDescriptor.Builder()
+ .name("Enable Advanced")
+ .description("Enable advanced settings")
+ .type(PropertyType.BOOLEAN)
+ .allowableValues("true", "false")
+ .defaultValue("false")
+ .build();
+
+ final ConnectorPropertyGroup group1 =
ConnectorPropertyGroup.builder()
+ .name("Basic Settings")
+ .addProperty(enableAdvanced)
+ .build();
+
+ final ConfigurationStep step1 = new ConfigurationStep.Builder()
+ .name("Step One")
+ .propertyGroups(List.of(group1))
+ .build();
+
+ final ConnectorPropertyDescriptor advancedProperty = new
ConnectorPropertyDescriptor.Builder()
+ .name("Advanced Setting")
+ .description("An advanced setting")
+ .type(PropertyType.STRING)
+ .build();
+
+ final ConnectorPropertyGroup group2 =
ConnectorPropertyGroup.builder()
+ .name("Advanced Settings")
+ .addProperty(advancedProperty)
+ .build();
+
+ final ConfigurationStep step2 = new ConfigurationStep.Builder()
+ .name("Step Two")
+ .propertyGroups(List.of(group2))
+ .dependsOn(step1, enableAdvanced, "true")
+ .build();
+
+ return List.of(step1, step2);
+ }
+ }
+
+ private static class ConnectorWithMultipleSteps extends MinimalConnector {
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyGroup connectionGroup =
ConnectorPropertyGroup.builder()
+ .name("Connection")
+ .build();
+
+ final ConnectorPropertyGroup authGroup =
ConnectorPropertyGroup.builder()
+ .name("Auth")
+ .build();
+
+ final ConnectorPropertyGroup advancedGroup =
ConnectorPropertyGroup.builder()
+ .name("Options")
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection")
+ .description("Connection settings")
+ .propertyGroups(List.of(connectionGroup))
+ .build(),
+ new ConfigurationStep.Builder()
+ .name("Authentication")
+ .description("Authentication settings")
+ .propertyGroups(List.of(authGroup))
+ .build(),
+ new ConfigurationStep.Builder()
+ .name("Advanced")
+ .description("Advanced settings")
+ .propertyGroups(List.of(advancedGroup))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithIntegerProperty extends MinimalConnector
{
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Port")
+ .description("The port number")
+ .type(PropertyType.INTEGER)
+ .defaultValue("8080")
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Connection")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Connection Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+
+ private static class ConnectorWithBooleanProperty extends MinimalConnector
{
+ @Override
+ public List<ConfigurationStep> getConfigurationSteps() {
+ final ConnectorPropertyDescriptor property = new
ConnectorPropertyDescriptor.Builder()
+ .name("Enabled")
+ .description("Whether the feature is enabled")
+ .type(PropertyType.BOOLEAN)
+ .defaultValue("true")
+ .build();
+
+ final ConnectorPropertyGroup group =
ConnectorPropertyGroup.builder()
+ .name("Settings")
+ .addProperty(property)
+ .build();
+
+ return List.of(
+ new ConfigurationStep.Builder()
+ .name("Settings Step")
+ .propertyGroups(List.of(group))
+ .build()
+ );
+ }
+ }
+}
+