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-12178-efc81bbb10102f0f4d8c8dec5040be4564221560 in repository https://gitbox.apache.org/repos/asf/seatunnel.git
commit f6ed9579c5e7f94e486e900b53d703ed2f5fa04b Author: Supidan <[email protected]> AuthorDate: Tue Sep 8 05:33:25 2026 +0000 [Improve][Connector-V2] Add nonblank validation for required Slack options (#12178) Co-authored-by: Supidan <[email protected]> --- .../seatunnel/slack/sink/SlackSinkFactory.java | 9 +- .../seatunnel/slack/SlackFactoryTest.java | 31 ------- .../seatunnel/slack/sink/SlackFactoryTest.java | 96 ++++++++++++++++++++++ 3 files changed, 101 insertions(+), 35 deletions(-) diff --git a/seatunnel-connectors-v2/connector-slack/src/main/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackSinkFactory.java b/seatunnel-connectors-v2/connector-slack/src/main/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackSinkFactory.java index 4fbe80562e..8dafb6ef3b 100644 --- a/seatunnel-connectors-v2/connector-slack/src/main/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackSinkFactory.java +++ b/seatunnel-connectors-v2/connector-slack/src/main/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackSinkFactory.java @@ -26,6 +26,8 @@ import org.apache.seatunnel.connectors.seatunnel.slack.config.SlackSinkOptions; import com.google.auto.service.AutoService; +import static org.apache.seatunnel.api.configuration.util.Conditions.notBlank; + @AutoService(Factory.class) public class SlackSinkFactory implements TableSinkFactory { @Override @@ -36,10 +38,9 @@ public class SlackSinkFactory implements TableSinkFactory { @Override public OptionRule optionRule() { return OptionRule.builder() - .required( - SlackSinkOptions.WEBHOOKS_URL, - SlackSinkOptions.OAUTH_TOKEN, - SlackSinkOptions.SLACK_CHANNEL) + .required(SlackSinkOptions.WEBHOOKS_URL, notBlank(SlackSinkOptions.WEBHOOKS_URL)) + .required(SlackSinkOptions.OAUTH_TOKEN, notBlank(SlackSinkOptions.OAUTH_TOKEN)) + .required(SlackSinkOptions.SLACK_CHANNEL, notBlank(SlackSinkOptions.SLACK_CHANNEL)) .build(); } diff --git a/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/SlackFactoryTest.java b/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/SlackFactoryTest.java deleted file mode 100644 index f7e553535d..0000000000 --- a/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/SlackFactoryTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.seatunnel.connectors.seatunnel.slack; - -import org.apache.seatunnel.connectors.seatunnel.slack.sink.SlackSinkFactory; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class SlackFactoryTest { - - @Test - void optionRule() { - Assertions.assertNotNull((new SlackSinkFactory()).optionRule()); - } -} diff --git a/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackFactoryTest.java b/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackFactoryTest.java new file mode 100644 index 0000000000..d99dfc813e --- /dev/null +++ b/seatunnel-connectors-v2/connector-slack/src/test/java/org/apache/seatunnel/connectors/seatunnel/slack/sink/SlackFactoryTest.java @@ -0,0 +1,96 @@ +/* + * 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.seatunnel.connectors.seatunnel.slack.sink; + +import org.apache.seatunnel.api.configuration.ReadonlyConfig; +import org.apache.seatunnel.api.configuration.util.ConfigValidator; +import org.apache.seatunnel.api.configuration.util.OptionValidationException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +class SlackFactoryTest { + + @Test + void validConfig() { + Assertions.assertDoesNotThrow(() -> validate(validOptions())); + } + + @ParameterizedTest + @ValueSource(strings = {"webhooks_url", "oauth_token", "slack_channel"}) + void missingRequiredOption(String key) { + Map<String, Object> options = validOptions(); + options.remove(key); + assertInvalid(options, key); + } + + @ParameterizedTest(name = "{0}: {1}") + @MethodSource("blankRequiredOptions") + void blankRequiredOption(String key, String description, String value) { + Map<String, Object> options = validOptions(); + options.put(key, value); + assertInvalid(options, key); + } + + private static Stream<Arguments> blankRequiredOptions() { + return Stream.of("webhooks_url", "oauth_token", "slack_channel") + .flatMap( + key -> + Stream.of( + Arguments.of(key, "empty", ""), + Arguments.of(key, "space", " "), + Arguments.of(key, "tab", "\t"), + Arguments.of(key, "newline", "\n"), + Arguments.of(key, "mixed whitespace", " \t\r\n "))); + } + + @ParameterizedTest + @ValueSource(strings = {"webhooks_url", "oauth_token", "slack_channel"}) + void preserveNonblankValues(String key) { + Map<String, Object> options = validOptions(); + options.put(key, " arbitrary nonblank value "); + Assertions.assertDoesNotThrow(() -> validate(options)); + } + + private void assertInvalid(Map<String, Object> options, String key) { + OptionValidationException exception = + Assertions.assertThrows(OptionValidationException.class, () -> validate(options)); + Assertions.assertTrue(exception.getMessage().contains(key)); + } + + private void validate(Map<String, Object> options) { + ConfigValidator.of(ReadonlyConfig.fromMap(options)) + .validate(new SlackSinkFactory().optionRule()); + } + + private Map<String, Object> validOptions() { + Map<String, Object> options = new HashMap<>(); + options.put("webhooks_url", "https://hooks.slack.com/services/test/test/test"); + options.put("oauth_token", "test-oauth-token"); + options.put("slack_channel", "seatunnel-alerts"); + return options; + } +}
