This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/dev/pr-12075-1e450e11f7c8e3ef78d186b0a407148d5faed939 in repository https://gitbox.apache.org/repos/asf/seatunnel.git
commit 98182ca59c93c10390717af4a49b8806f5c555f1 Author: Nikhil kumar <[email protected]> AuthorDate: Fri Sep 4 03:50:30 2026 +0000 [Improve][Connector-V2] Migrate HugeGraph sink validation to OptionRule (#12075) --- .../hugegraph/sink/HugeGraphSinkFactory.java | 25 ++++-- .../hugegraph/sink/HugeGraphSinkFactoryTest.java | 95 +++++++++++++++++++++- 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/seatunnel-connectors-v2/connector-hugegraph/src/main/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactory.java b/seatunnel-connectors-v2/connector-hugegraph/src/main/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactory.java index fa4630a558..25140b8507 100644 --- a/seatunnel-connectors-v2/connector-hugegraph/src/main/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactory.java +++ b/seatunnel-connectors-v2/connector-hugegraph/src/main/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactory.java @@ -29,6 +29,11 @@ import org.apache.seatunnel.connectors.seatunnel.hugegraph.config.HugeGraphSinkO import com.google.auto.service.AutoService; +import static org.apache.seatunnel.api.configuration.util.Conditions.greaterOrEqual; +import static org.apache.seatunnel.api.configuration.util.Conditions.lessOrEqual; +import static org.apache.seatunnel.api.configuration.util.Conditions.matches; +import static org.apache.seatunnel.api.configuration.util.Conditions.notBlank; + @AutoService(Factory.class) public class HugeGraphSinkFactory implements TableSinkFactory { @@ -47,11 +52,16 @@ public class HugeGraphSinkFactory implements TableSinkFactory { public OptionRule optionRule() { return OptionRule.builder() // connection config - .required(HugeGraphOptions.HOST, HugeGraphOptions.PORT, HugeGraphOptions.GRAPH_NAME) + .required(HugeGraphOptions.HOST, notBlank(HugeGraphOptions.HOST)) + .required( + HugeGraphOptions.PORT, + greaterOrEqual(HugeGraphOptions.PORT, 1), + lessOrEqual(HugeGraphOptions.PORT, 65535)) + .required(HugeGraphOptions.GRAPH_NAME, notBlank(HugeGraphOptions.GRAPH_NAME)) + .bundled(HugeGraphOptions.USERNAME, HugeGraphOptions.PASSWORD) + .optional( + HugeGraphOptions.PROTOCOL, matches(HugeGraphOptions.PROTOCOL, "(?i)https?")) .optional( - HugeGraphOptions.PROTOCOL, - HugeGraphOptions.USERNAME, - HugeGraphOptions.PASSWORD, // Optional connection setting passed through to select the HugeGraph graph // space (defaults to "DEFAULT"). HugeGraphOptions.GRAPH_SPACE) @@ -79,8 +89,13 @@ public class HugeGraphSinkFactory implements TableSinkFactory { // retry config .optional( HugeGraphOptions.MAX_RETRIES, + greaterOrEqual(HugeGraphOptions.MAX_RETRIES, 0)) + .optional( HugeGraphOptions.RETRY_BACKOFF_MS, - HugeGraphOptions.RETRY_BACKOFF_MAX_MS) + greaterOrEqual(HugeGraphOptions.RETRY_BACKOFF_MS, 0)) + .optional( + HugeGraphOptions.RETRY_BACKOFF_MAX_MS, + greaterOrEqual(HugeGraphOptions.RETRY_BACKOFF_MAX_MS, 0)) // deprecated field selection .optional(HugeGraphSinkOptions.SELECTED_FIELDS, HugeGraphSinkOptions.IGNORED_FIELDS) .build(); diff --git a/seatunnel-connectors-v2/connector-hugegraph/src/test/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactoryTest.java b/seatunnel-connectors-v2/connector-hugegraph/src/test/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactoryTest.java index b8db1995eb..4f5700ecc9 100644 --- a/seatunnel-connectors-v2/connector-hugegraph/src/test/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactoryTest.java +++ b/seatunnel-connectors-v2/connector-hugegraph/src/test/java/org/apache/seatunnel/connectors/seatunnel/hugegraph/sink/HugeGraphSinkFactoryTest.java @@ -18,17 +18,27 @@ package org.apache.seatunnel.connectors.seatunnel.hugegraph.sink; import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.ReadonlyConfig; +import org.apache.seatunnel.api.configuration.util.ConfigValidator; +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.api.configuration.util.OptionValidationException; import org.apache.seatunnel.connectors.seatunnel.hugegraph.config.HugeGraphOptions; import org.apache.seatunnel.connectors.seatunnel.hugegraph.config.HugeGraphSinkOptions; import org.junit.jupiter.api.Test; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class HugeGraphSinkFactoryTest { + private final OptionRule optionRule = new HugeGraphSinkFactory().optionRule(); + /** * Every option the sink actually reads must be declared in {@code optionRule()}; otherwise * {@code seatunnel.sh --config x --check} (and STATIC dry-run) reject it as an unknown key and @@ -36,7 +46,7 @@ class HugeGraphSinkFactoryTest { */ @Test void optionRuleDeclaresAllReadOptions() { - List<Option<?>> optional = new HugeGraphSinkFactory().optionRule().getOptionalOptions(); + List<Option<?>> optional = optionRule.getOptionalOptions(); assertTrue( optional.contains(HugeGraphSinkOptions.DATA_SAVE_MODE), "data_save_mode missing"); assertTrue(optional.contains(HugeGraphOptions.CHECK_VERTEX), "check_vertex missing"); @@ -51,4 +61,87 @@ class HugeGraphSinkFactoryTest { optional.contains(HugeGraphOptions.RETRY_BACKOFF_MAX_MS), "retry_backoff_max_ms missing"); } + + @Test + void acceptsValidConnectionOptions() { + assertDoesNotThrow(() -> validate(requiredConfig())); + + Map<String, Object> config = requiredConfig(); + config.put(HugeGraphOptions.PROTOCOL.key(), "HTTPS"); + config.put(HugeGraphOptions.USERNAME.key(), "user"); + config.put(HugeGraphOptions.PASSWORD.key(), "password"); + config.put(HugeGraphOptions.MAX_RETRIES.key(), 0); + config.put(HugeGraphOptions.RETRY_BACKOFF_MS.key(), 0); + config.put(HugeGraphOptions.RETRY_BACKOFF_MAX_MS.key(), 0); + + assertDoesNotThrow(() -> validate(config)); + } + + @Test + void rejectsBlankRequiredConnectionOptions() { + Map<String, Object> blankHost = requiredConfig(); + blankHost.put(HugeGraphOptions.HOST.key(), " "); + assertThrows(OptionValidationException.class, () -> validate(blankHost)); + + Map<String, Object> blankGraphName = requiredConfig(); + blankGraphName.put(HugeGraphOptions.GRAPH_NAME.key(), "\t"); + assertThrows(OptionValidationException.class, () -> validate(blankGraphName)); + } + + @Test + void rejectsPortOutsideValidRange() { + Map<String, Object> portTooLow = requiredConfig(); + portTooLow.put(HugeGraphOptions.PORT.key(), 0); + assertThrows(OptionValidationException.class, () -> validate(portTooLow)); + + Map<String, Object> portTooHigh = requiredConfig(); + portTooHigh.put(HugeGraphOptions.PORT.key(), 65536); + assertThrows(OptionValidationException.class, () -> validate(portTooHigh)); + } + + @Test + void rejectsUnsupportedProtocol() { + Map<String, Object> config = requiredConfig(); + config.put(HugeGraphOptions.PROTOCOL.key(), "ftp"); + + assertThrows(OptionValidationException.class, () -> validate(config)); + } + + @Test + void requiresCredentialsToBeConfiguredTogether() { + Map<String, Object> usernameOnly = requiredConfig(); + usernameOnly.put(HugeGraphOptions.USERNAME.key(), "user"); + assertThrows(OptionValidationException.class, () -> validate(usernameOnly)); + + Map<String, Object> passwordOnly = requiredConfig(); + passwordOnly.put(HugeGraphOptions.PASSWORD.key(), "password"); + assertThrows(OptionValidationException.class, () -> validate(passwordOnly)); + } + + @Test + void rejectsNegativeRetryOptions() { + assertNegativeOptionRejected(HugeGraphOptions.MAX_RETRIES); + assertNegativeOptionRejected(HugeGraphOptions.RETRY_BACKOFF_MS); + assertNegativeOptionRejected(HugeGraphOptions.RETRY_BACKOFF_MAX_MS); + } + + private void assertNegativeOptionRejected(Option<Integer> option) { + Map<String, Object> config = requiredConfig(); + config.put(option.key(), -1); + assertThrows(OptionValidationException.class, () -> validate(config)); + } + + private void validate(Map<String, Object> config) { + ReadonlyConfig readonlyConfig = ReadonlyConfig.fromMap(config); + ConfigValidator.validateUnknownKeys(readonlyConfig, optionRule, "HugeGraphSink"); + ConfigValidator.of(readonlyConfig).validate(optionRule); + } + + private static Map<String, Object> requiredConfig() { + Map<String, Object> config = new HashMap<>(); + config.put(HugeGraphOptions.HOST.key(), "127.0.0.1"); + config.put(HugeGraphOptions.PORT.key(), 8080); + config.put(HugeGraphOptions.GRAPH_NAME.key(), "hugegraph"); + return config; + } }
