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;
+    }
+}

Reply via email to