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-12352-2a8abf1e65971dd9c91b2ef69b2c90b747b2e0fb
in repository https://gitbox.apache.org/repos/asf/seatunnel.git

commit 6eea1da1a8c10e1ed048ce59db34d2f13ae685dc
Author: Nikhil kumar <[email protected]>
AuthorDate: Wed Sep 23 10:23:15 2026 +0000

    [Improve][Connector-V2] Validate SLS required options (#12352)
---
 docs/en/connectors/sink/Sls.md                     | 10 +--
 docs/en/connectors/source/Sls.md                   | 10 +--
 docs/zh/connectors/sink/Sls.md                     | 10 +--
 docs/zh/connectors/source/Sls.md                   | 10 +--
 .../seatunnel/sls/sink/SlsSinkFactory.java         | 13 +--
 .../seatunnel/sls/source/SlsSourceFactory.java     | 13 +--
 .../connectors/seatunnel/sls/SlsFactoryTest.java   | 94 +++++++++++++++++++++-
 7 files changed, 127 insertions(+), 33 deletions(-)

diff --git a/docs/en/connectors/sink/Sls.md b/docs/en/connectors/sink/Sls.md
index 3c18322d45..77bc18be18 100644
--- a/docs/en/connectors/sink/Sls.md
+++ b/docs/en/connectors/sink/Sls.md
@@ -34,11 +34,11 @@ Maven central repository.
 
 | Name              | Type   | Required | Default            | Description     
                                                                                
        |
 
|-------------------|--------|----------|--------------------|---------------------------------------------------------------------------------------------------------|
-| endpoint          | String | Yes      | -                  | Alibaba Cloud 
SLS endpoint, for example `cn-hangzhou.log.aliyuncs.com` or an intranet 
endpoint.         |
-| project           | String | Yes      | -                  | [Alibaba Cloud 
SLS project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project).       
         |
-| logstore          | String | Yes      | -                  | [Alibaba Cloud 
SLS logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore).     
         |
-| access_key_id     | String | Yes      | -                  | Alibaba Cloud 
AccessKey ID.                                                                   
          |
-| access_key_secret | String | Yes      | -                  | Alibaba Cloud 
AccessKey secret.                                                               
          |
+| endpoint          | String | Yes      | -                  | Alibaba Cloud 
SLS endpoint, for example `cn-hangzhou.log.aliyuncs.com` or an intranet 
endpoint. Must not be empty or whitespace-only. |
+| project           | String | Yes      | -                  | [Alibaba Cloud 
SLS project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project). Must 
not be empty or whitespace-only.        |
+| logstore          | String | Yes      | -                  | [Alibaba Cloud 
SLS logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore). 
Must not be empty or whitespace-only.      |
+| access_key_id     | String | Yes      | -                  | Alibaba Cloud 
AccessKey ID. Must not be empty or whitespace-only.                             
                                       |
+| access_key_secret | String | Yes      | -                  | Alibaba Cloud 
AccessKey secret. Must not be empty or whitespace-only.                         
                                       |
 | source            | String | No       | `SeaTunnel-Source` | Source tag 
written to SLS log groups.                                                      
             |
 | topic             | String | No       | `SeaTunnel-Topic`  | Topic tag 
written to SLS log groups.                                                      
              |
 
diff --git a/docs/en/connectors/source/Sls.md b/docs/en/connectors/source/Sls.md
index 161b00ca6a..0e03a529c0 100644
--- a/docs/en/connectors/source/Sls.md
+++ b/docs/en/connectors/source/Sls.md
@@ -43,11 +43,11 @@ Maven central repository.
 
 | Name                                | Type                                   
   | Required | Default                  | Description                          
                                                                                
                            |
 
|-------------------------------------|-------------------------------------------|----------|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
-| endpoint                            | String                                 
   | Yes      | -                        | Alibaba Cloud SLS endpoint, for 
example `cn-hangzhou.log.aliyuncs.com` or an intranet endpoint.                 
                                 |
-| project                             | String                                 
   | Yes      | -                        | [Alibaba Cloud SLS 
project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project).           
                                              |
-| logstore                            | String                                 
   | Yes      | -                        | [Alibaba Cloud SLS 
logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore).         
                                              |
-| access_key_id                       | String                                 
   | Yes      | -                        | Alibaba Cloud AccessKey ID.          
                                                                                
                            |
-| access_key_secret                   | String                                 
   | Yes      | -                        | Alibaba Cloud AccessKey secret.      
                                                                                
                            |
+| endpoint                            | String                                 
   | Yes      | -                        | Alibaba Cloud SLS endpoint, for 
example `cn-hangzhou.log.aliyuncs.com` or an intranet endpoint. Must not be 
empty or whitespace-only.            |
+| project                             | String                                 
   | Yes      | -                        | [Alibaba Cloud SLS 
project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project). Must not 
be empty or whitespace-only.                   |
+| logstore                            | String                                 
   | Yes      | -                        | [Alibaba Cloud SLS 
logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore). Must 
not be empty or whitespace-only.                 |
+| access_key_id                       | String                                 
   | Yes      | -                        | Alibaba Cloud AccessKey ID. Must not 
be empty or whitespace-only.                                                    
                            |
+| access_key_secret                   | String                                 
   | Yes      | -                        | Alibaba Cloud AccessKey secret. Must 
not be empty or whitespace-only.                                                
                            |
 | start_mode                          | `earliest`, `group_cursor`, `latest`   
   | No       | `group_cursor`           | Initial cursor mode. `earliest` 
starts from the beginning, `latest` starts from the end, and `group_cursor` 
uses the consumer group's checkpoint. |
 | consumer_group                      | String                                 
   | No       | `SeaTunnel-Consumer-Group` | SLS consumer group name. Use 
different values when separate jobs must keep independent cursors.              
                                     |
 | auto_cursor_reset                   | `begin`, `end`                         
   | No       | `end`                    | Cursor position used when 
`start_mode = group_cursor` but the consumer group has no checkpoint yet.       
                                        |
diff --git a/docs/zh/connectors/sink/Sls.md b/docs/zh/connectors/sink/Sls.md
index e398948446..3985bf21d0 100644
--- a/docs/zh/connectors/sink/Sls.md
+++ b/docs/zh/connectors/sink/Sls.md
@@ -33,11 +33,11 @@ JSON,然后作为 SLS 日志项写入,日志内容的 key 为 `content`。
 
 | 名称              | 类型   | 是否必填 | 默认值             | 描述                         
                                                                          |
 
|-------------------|--------|----------|--------------------|--------------------------------------------------------------------------------------------------------|
-| endpoint          | String | 是       | -                  | 阿里云 SLS 访问地址,例如 
`cn-hangzhou.log.aliyuncs.com` 或内网访问地址。                              |
-| project           | String | 是       | -                  | [阿里云 SLS 
Project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project)。           
           |
-| logstore          | String | 是       | -                  | [阿里云 SLS 
Logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore)。         
           |
-| access_key_id     | String | 是       | -                  | 阿里云 AccessKey 
ID。                                                                             
     |
-| access_key_secret | String | 是       | -                  | 阿里云 AccessKey 
Secret。                                                                         
     |
+| endpoint          | String | 是       | -                  | 阿里云 SLS 访问地址,例如 
`cn-hangzhou.log.aliyuncs.com` 或内网访问地址。值不能为空或仅包含空白字符。   |
+| project           | String | 是       | -                  | [阿里云 SLS 
Project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project)。值不能为空或仅包含空白字符。
 |
+| logstore          | String | 是       | -                  | [阿里云 SLS 
Logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore)。值不能为空或仅包含空白字符。
 |
+| access_key_id     | String | 是       | -                  | 阿里云 AccessKey 
ID。值不能为空或仅包含空白字符。                                                  |
+| access_key_secret | String | 是       | -                  | 阿里云 AccessKey 
Secret。值不能为空或仅包含空白字符。                                              |
 | source            | String | 否       | `SeaTunnel-Source` | 写入 SLS log group 
的 source 标记。                                                                    
|
 | topic             | String | 否       | `SeaTunnel-Topic`  | 写入 SLS log group 
的 topic 标记。                                                                     
|
 
diff --git a/docs/zh/connectors/source/Sls.md b/docs/zh/connectors/source/Sls.md
index 54db0f1538..16424c3fa2 100644
--- a/docs/zh/connectors/source/Sls.md
+++ b/docs/zh/connectors/source/Sls.md
@@ -42,11 +42,11 @@ SLS shard 并行读取。流处理模式下,SeaTunnel 会在 checkpoint 完成
 
 | 名称                                | 类型                                      
| 是否必填 | 默认值                     | 描述                                           
                                                                |
 
|-------------------------------------|-------------------------------------------|----------|----------------------------|----------------------------------------------------------------------------------------------------------------|
-| endpoint                            | String                                 
   | 是       | -                          | 阿里云 SLS 访问地址,例如 
`cn-hangzhou.log.aliyuncs.com` 或内网访问地址。                                      |
-| project                             | String                                 
   | 是       | -                          | [阿里云 SLS 
Project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project)。           
                   |
-| logstore                            | String                                 
   | 是       | -                          | [阿里云 SLS 
Logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore)。         
                   |
-| access_key_id                       | String                                 
   | 是       | -                          | 阿里云 AccessKey ID。                   
                                                                       |
-| access_key_secret                   | String                                 
   | 是       | -                          | 阿里云 AccessKey Secret。               
                                                                       |
+| endpoint                            | String                                 
   | 是       | -                          | 阿里云 SLS 访问地址,例如 
`cn-hangzhou.log.aliyuncs.com` 或内网访问地址。值不能为空或仅包含空白字符。           |
+| project                             | String                                 
   | 是       | -                          | [阿里云 SLS 
Project](https://help.aliyun.com/zh/sls/user-guide/manage-a-project)。值不能为空或仅包含空白字符。
  |
+| logstore                            | String                                 
   | 是       | -                          | [阿里云 SLS 
Logstore](https://help.aliyun.com/zh/sls/user-guide/manage-a-logstore)。值不能为空或仅包含空白字符。|
+| access_key_id                       | String                                 
   | 是       | -                          | 阿里云 AccessKey ID。值不能为空或仅包含空白字符。     
                                                         |
+| access_key_secret                   | String                                 
   | 是       | -                          | 阿里云 AccessKey Secret。值不能为空或仅包含空白字符。 
                                                         |
 | start_mode                          | `earliest`, `group_cursor`, `latest`   
   | 否       | `group_cursor`             | 初始读取位置。`earliest` 从最早位置读取,`latest` 
从最新位置读取,`group_cursor` 使用消费者组已提交的游标。     |
 | consumer_group                      | String                                 
   | 否       | `SeaTunnel-Consumer-Group` | SLS 
消费者组名称。不同任务如果需要独立保存读取位置,应该使用不同的消费者组。                                   |
 | auto_cursor_reset                   | `begin`, `end`                         
   | 否       | `end`                      | 当 `start_mode = group_cursor` 
但消费者组还没有已提交游标时,使用的初始化位置。                                |
diff --git 
a/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/sink/SlsSinkFactory.java
 
b/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/sink/SlsSinkFactory.java
index 099da96baa..a869bc8a90 100644
--- 
a/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/sink/SlsSinkFactory.java
+++ 
b/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/sink/SlsSinkFactory.java
@@ -26,6 +26,8 @@ import 
org.apache.seatunnel.connectors.seatunnel.sls.config.SlsSinkOptions;
 
 import com.google.auto.service.AutoService;
 
+import static org.apache.seatunnel.api.configuration.util.Conditions.notBlank;
+
 @AutoService(Factory.class)
 public class SlsSinkFactory implements TableSinkFactory {
     @Override
@@ -36,12 +38,13 @@ public class SlsSinkFactory implements TableSinkFactory {
     @Override
     public OptionRule optionRule() {
         return OptionRule.builder()
+                .required(SlsSinkOptions.ENDPOINT, 
notBlank(SlsSinkOptions.ENDPOINT))
+                .required(SlsSinkOptions.PROJECT, 
notBlank(SlsSinkOptions.PROJECT))
+                .required(SlsSinkOptions.LOGSTORE, 
notBlank(SlsSinkOptions.LOGSTORE))
+                .required(SlsSinkOptions.ACCESS_KEY_ID, 
notBlank(SlsSinkOptions.ACCESS_KEY_ID))
                 .required(
-                        SlsSinkOptions.ENDPOINT,
-                        SlsSinkOptions.PROJECT,
-                        SlsSinkOptions.LOGSTORE,
-                        SlsSinkOptions.ACCESS_KEY_ID,
-                        SlsSinkOptions.ACCESS_KEY_SECRET)
+                        SlsSinkOptions.ACCESS_KEY_SECRET,
+                        notBlank(SlsSinkOptions.ACCESS_KEY_SECRET))
                 .optional(SlsSinkOptions.SOURCE, SlsSinkOptions.TOPIC)
                 .build();
     }
diff --git 
a/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/source/SlsSourceFactory.java
 
b/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/source/SlsSourceFactory.java
index 5e698bc498..bce62586d2 100644
--- 
a/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/source/SlsSourceFactory.java
+++ 
b/seatunnel-connectors-v2/connector-sls/src/main/java/org/apache/seatunnel/connectors/seatunnel/sls/source/SlsSourceFactory.java
@@ -30,6 +30,8 @@ import com.google.auto.service.AutoService;
 
 import java.io.Serializable;
 
+import static org.apache.seatunnel.api.configuration.util.Conditions.notBlank;
+
 @AutoService(Factory.class)
 public class SlsSourceFactory implements TableSourceFactory {
     @Override
@@ -45,12 +47,13 @@ public class SlsSourceFactory implements TableSourceFactory 
{
     @Override
     public OptionRule optionRule() {
         return OptionRule.builder()
+                .required(SlsSourceOptions.ENDPOINT, 
notBlank(SlsSourceOptions.ENDPOINT))
+                .required(SlsSourceOptions.PROJECT, 
notBlank(SlsSourceOptions.PROJECT))
+                .required(SlsSourceOptions.LOGSTORE, 
notBlank(SlsSourceOptions.LOGSTORE))
+                .required(SlsSourceOptions.ACCESS_KEY_ID, 
notBlank(SlsSourceOptions.ACCESS_KEY_ID))
                 .required(
-                        SlsSourceOptions.ENDPOINT,
-                        SlsSourceOptions.PROJECT,
-                        SlsSourceOptions.LOGSTORE,
-                        SlsSourceOptions.ACCESS_KEY_ID,
-                        SlsSourceOptions.ACCESS_KEY_SECRET)
+                        SlsSourceOptions.ACCESS_KEY_SECRET,
+                        notBlank(SlsSourceOptions.ACCESS_KEY_SECRET))
                 .optional(
                         SlsSourceOptions.BATCH_SIZE,
                         SlsSourceOptions.START_MODE,
diff --git 
a/seatunnel-connectors-v2/connector-sls/src/test/java/org/apache/seatunnel/connectors/seatunnel/sls/SlsFactoryTest.java
 
b/seatunnel-connectors-v2/connector-sls/src/test/java/org/apache/seatunnel/connectors/seatunnel/sls/SlsFactoryTest.java
index 84780c77dd..e8130e3c0c 100644
--- 
a/seatunnel-connectors-v2/connector-sls/src/test/java/org/apache/seatunnel/connectors/seatunnel/sls/SlsFactoryTest.java
+++ 
b/seatunnel-connectors-v2/connector-sls/src/test/java/org/apache/seatunnel/connectors/seatunnel/sls/SlsFactoryTest.java
@@ -17,17 +17,105 @@
 
 package org.apache.seatunnel.connectors.seatunnel.sls;
 
+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.sls.config.SlsBaseOptions;
 import org.apache.seatunnel.connectors.seatunnel.sls.sink.SlsSinkFactory;
 import org.apache.seatunnel.connectors.seatunnel.sls.source.SlsSourceFactory;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 public class SlsFactoryTest {
 
+    private static final OptionRule SOURCE_OPTION_RULE = new 
SlsSourceFactory().optionRule();
+    private static final OptionRule SINK_OPTION_RULE = new 
SlsSinkFactory().optionRule();
+    private static final List<String> REQUIRED_STRING_KEYS =
+            Arrays.asList(
+                    SlsBaseOptions.ENDPOINT.key(),
+                    SlsBaseOptions.PROJECT.key(),
+                    SlsBaseOptions.LOGSTORE.key(),
+                    SlsBaseOptions.ACCESS_KEY_ID.key(),
+                    SlsBaseOptions.ACCESS_KEY_SECRET.key());
+
+    @Test
+    void testValidSourceConfiguration() {
+        assertValidRequiredStrings(SOURCE_OPTION_RULE, "SlsSource");
+    }
+
+    @Test
+    void testInvalidSourceConfiguration() {
+        assertInvalidRequiredStrings(SOURCE_OPTION_RULE, "SlsSource");
+    }
+
+    @Test
+    void testValidSinkConfiguration() {
+        assertValidRequiredStrings(SINK_OPTION_RULE, "SlsSink");
+    }
+
     @Test
-    void optionRule() {
-        Assertions.assertNotNull((new SlsSourceFactory()).optionRule());
-        Assertions.assertNotNull((new SlsSinkFactory()).optionRule());
+    void testInvalidSinkConfiguration() {
+        assertInvalidRequiredStrings(SINK_OPTION_RULE, "SlsSink");
+    }
+
+    private void assertValidRequiredStrings(OptionRule optionRule, String 
factoryName) {
+        Assertions.assertDoesNotThrow(() -> validate(validConfig(), 
optionRule, factoryName));
+
+        Map<String, Object> valuesWithSurroundingSpaces = validConfig();
+        for (String key : REQUIRED_STRING_KEYS) {
+            valuesWithSurroundingSpaces.put(key, " " + 
valuesWithSurroundingSpaces.get(key) + " ");
+        }
+        Assertions.assertDoesNotThrow(
+                () -> validate(valuesWithSurroundingSpaces, optionRule, 
factoryName));
+    }
+
+    private void assertInvalidRequiredStrings(OptionRule optionRule, String 
factoryName) {
+        for (String key : REQUIRED_STRING_KEYS) {
+            Map<String, Object> missing = validConfig();
+            missing.remove(key);
+            assertInvalid(missing, optionRule, factoryName, key);
+
+            Map<String, Object> empty = validConfig();
+            empty.put(key, "");
+            assertInvalid(empty, optionRule, factoryName, key);
+
+            Map<String, Object> whitespaceOnly = validConfig();
+            whitespaceOnly.put(key, " \t\n ");
+            assertInvalid(whitespaceOnly, optionRule, factoryName, key);
+        }
+    }
+
+    private void assertInvalid(
+            Map<String, Object> config,
+            OptionRule optionRule,
+            String factoryName,
+            String optionKey) {
+        Assertions.assertThrows(
+                OptionValidationException.class,
+                () -> validate(config, optionRule, factoryName),
+                optionKey);
+    }
+
+    private void validate(Map<String, Object> config, OptionRule optionRule, 
String factoryName) {
+        ReadonlyConfig readonlyConfig = ReadonlyConfig.fromMap(config);
+        ConfigValidator.validateUnknownKeys(readonlyConfig, optionRule, 
factoryName);
+        ConfigValidator.of(readonlyConfig).validate(optionRule);
+    }
+
+    private Map<String, Object> validConfig() {
+        Map<String, Object> config = new HashMap<>();
+        config.put(SlsBaseOptions.ENDPOINT.key(), 
"cn-hangzhou.log.aliyuncs.com");
+        config.put(SlsBaseOptions.PROJECT.key(), "seatunnel-project");
+        config.put(SlsBaseOptions.LOGSTORE.key(), "seatunnel-logstore");
+        config.put(SlsBaseOptions.ACCESS_KEY_ID.key(), "access-key-id");
+        config.put(SlsBaseOptions.ACCESS_KEY_SECRET.key(), 
"access-key-secret");
+        return config;
     }
 }

Reply via email to