Skip to content

Commit 65e32aa

Browse files
[Improve][Connector-V2] Migrate email validation to declarative OptionRule (#11817)
1 parent da7b1e0 commit 65e32aa

4 files changed

Lines changed: 81 additions & 5 deletions

File tree

docs/en/connectors/sink/Email.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ password. When set to `false`, the connector sends mail over plain SMTP without
6464

6565
### email_smtp_port [int]
6666

67-
SMTP server port. The default `465` is the SMTPS port and is used together with
67+
SMTP server port. The value must be between `1` and `65535`, inclusive. The default `465` is the SMTPS port and is used together with
6868
`email_smtp_auth = true`. For plain SMTP without authentication, set the port that matches the
6969
server (for example `25` or `3025`).
7070

docs/zh/connectors/sink/Email.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import ChangeLog from '../changelog/connector-email.md';
6262

6363
### email_smtp_port [int]
6464

65-
SMTP 服务器端口。默认值 `465` 为 SMTPS 端口,需与 `email_smtp_auth = true` 配合使用。如果使用不带认证的普通 SMTP,请填写与服务匹配的端口(例如 `25``3025`)。
65+
SMTP 服务器端口,取值必须在 `1``65535` 之间(包含边界值)。默认值 `465` 为 SMTPS 端口,需与 `email_smtp_auth = true` 配合使用。如果使用不带认证的普通 SMTP,请填写与服务匹配的端口(例如 `25``3025`)。
6666

6767
### email_authorization_code [string]
6868

seatunnel-connectors-v2/connector-email/src/main/java/org/apache/seatunnel/connectors/seatunnel/email/sink/EmailSinkFactory.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@
2727

2828
import com.google.auto.service.AutoService;
2929

30+
import static org.apache.seatunnel.api.configuration.util.Conditions.greaterThan;
31+
import static org.apache.seatunnel.api.configuration.util.Conditions.lessOrEqual;
32+
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_ATTACHMENT_NAME;
3033
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_AUTHORIZATION_CODE;
34+
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_FIELD_DELIMITER;
3135
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_FROM_ADDRESS;
3236
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_HOST;
3337
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_MESSAGE_CONTENT;
3438
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_MESSAGE_HEADLINE;
3539
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_SMTP_AUTH;
40+
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_SMTP_PORT;
3641
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_TO_ADDRESS;
3742
import static org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions.EMAIL_TRANSPORT_PROTOCOL;
3843

@@ -61,7 +66,14 @@ public OptionRule optionRule() {
6166
EMAIL_AUTHORIZATION_CODE,
6267
EMAIL_MESSAGE_HEADLINE,
6368
EMAIL_MESSAGE_CONTENT)
64-
.optional(SinkConnectorCommonOptions.MULTI_TABLE_SINK_REPLICA)
69+
// Fail fast during option validation instead of when the writer sends the email.
70+
.optional(
71+
EMAIL_SMTP_PORT,
72+
greaterThan(EMAIL_SMTP_PORT, 0).and(lessOrEqual(EMAIL_SMTP_PORT, 65535)))
73+
.optional(
74+
EMAIL_ATTACHMENT_NAME,
75+
EMAIL_FIELD_DELIMITER,
76+
SinkConnectorCommonOptions.MULTI_TABLE_SINK_REPLICA)
6577
.build();
6678
}
6779
}

seatunnel-connectors-v2/connector-email/src/test/java/org/apache/seatunnel/connectors/seatunnel/email/EmailFactoryTest.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,79 @@
1717

1818
package org.apache.seatunnel.connectors.seatunnel.email;
1919

20+
import org.apache.seatunnel.api.configuration.ReadonlyConfig;
21+
import org.apache.seatunnel.api.configuration.util.ConfigValidator;
22+
import org.apache.seatunnel.api.configuration.util.OptionRule;
23+
import org.apache.seatunnel.api.configuration.util.OptionValidationException;
24+
import org.apache.seatunnel.connectors.seatunnel.email.config.EmailSinkOptions;
2025
import org.apache.seatunnel.connectors.seatunnel.email.sink.EmailSinkFactory;
2126

2227
import org.junit.jupiter.api.Assertions;
2328
import org.junit.jupiter.api.Test;
2429

30+
import java.util.HashMap;
31+
import java.util.Map;
32+
2533
public class EmailFactoryTest {
2634

35+
private final OptionRule optionRule = new EmailSinkFactory().optionRule();
36+
37+
@Test
38+
void testValidSmtpPorts() {
39+
Map<String, Object> config = requiredConfig();
40+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 1);
41+
Assertions.assertDoesNotThrow(() -> validate(config));
42+
43+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 465);
44+
Assertions.assertDoesNotThrow(() -> validate(config));
45+
46+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 65535);
47+
Assertions.assertDoesNotThrow(() -> validate(config));
48+
}
49+
50+
@Test
51+
void testDefaultOptionalOptions() {
52+
Assertions.assertDoesNotThrow(() -> validate(requiredConfig()));
53+
}
54+
2755
@Test
28-
void optionRule() {
29-
Assertions.assertNotNull((new EmailSinkFactory()).optionRule());
56+
void testExplicitOptionalOptions() {
57+
Map<String, Object> config = requiredConfig();
58+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 465);
59+
config.put(EmailSinkOptions.EMAIL_ATTACHMENT_NAME.key(), "report.csv");
60+
config.put(EmailSinkOptions.EMAIL_FIELD_DELIMITER.key(), "|");
61+
Assertions.assertDoesNotThrow(() -> validate(config));
62+
}
63+
64+
@Test
65+
void testInvalidSmtpPorts() {
66+
Map<String, Object> config = requiredConfig();
67+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 0);
68+
Assertions.assertThrows(OptionValidationException.class, () -> validate(config));
69+
70+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), -1);
71+
Assertions.assertThrows(OptionValidationException.class, () -> validate(config));
72+
73+
config.put(EmailSinkOptions.EMAIL_SMTP_PORT.key(), 65536);
74+
Assertions.assertThrows(OptionValidationException.class, () -> validate(config));
75+
}
76+
77+
private void validate(Map<String, Object> config) {
78+
ReadonlyConfig readonlyConfig = ReadonlyConfig.fromMap(config);
79+
ConfigValidator.validateUnknownKeys(readonlyConfig, optionRule, "EmailSink");
80+
ConfigValidator.of(readonlyConfig).validate(optionRule);
81+
}
82+
83+
private Map<String, Object> requiredConfig() {
84+
Map<String, Object> config = new HashMap<>();
85+
config.put(EmailSinkOptions.EMAIL_FROM_ADDRESS.key(), "sender@example.com");
86+
config.put(EmailSinkOptions.EMAIL_TO_ADDRESS.key(), "receiver@example.com");
87+
config.put(EmailSinkOptions.EMAIL_HOST.key(), "smtp.example.com");
88+
config.put(EmailSinkOptions.EMAIL_TRANSPORT_PROTOCOL.key(), "smtp");
89+
config.put(EmailSinkOptions.EMAIL_SMTP_AUTH.key(), true);
90+
config.put(EmailSinkOptions.EMAIL_AUTHORIZATION_CODE.key(), "code");
91+
config.put(EmailSinkOptions.EMAIL_MESSAGE_HEADLINE.key(), "subject");
92+
config.put(EmailSinkOptions.EMAIL_MESSAGE_CONTENT.key(), "content");
93+
return config;
3094
}
3195
}

0 commit comments

Comments
 (0)