Skip to content

Commit 4054c90

Browse files
committed
Refine robot config UX and simplify test response JSON
1 parent 04d252d commit 4054c90

File tree

17 files changed

+258
-71
lines changed

17 files changed

+258
-71
lines changed

src/main/java/io/jenkins/plugins/lark/notice/config/LarkGlobalConfig.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.jenkins.plugins.lark.notice.config;
22

33
import hudson.Extension;
4+
import io.jenkins.plugins.lark.notice.Messages;
45
import io.jenkins.plugins.lark.notice.config.LarkRobotConfig.LarkRobotConfigDescriptor;
56
import io.jenkins.plugins.lark.notice.config.security.LarkPermissions;
67
import io.jenkins.plugins.lark.notice.enums.NoticeOccasionEnum;
@@ -137,11 +138,15 @@ public boolean configure(StaplerRequest2 req, JSONObject json) throws FormExcept
137138
json.put("robotConfigs", new JSONArray());
138139
} else {
139140
JSONArray robotConfigs = JSONArray.fromObject(robotConfigObj);
140-
robotConfigs.removeIf(item -> {
141-
JSONObject jsonObject = JSONObject.fromObject(item);
142-
String webhook = jsonObject.getString("webhook");
143-
return StringUtils.isEmpty(webhook);
144-
});
141+
for (int i = 0; i < robotConfigs.size(); i++) {
142+
JSONObject jsonObject = JSONObject.fromObject(robotConfigs.get(i));
143+
Object webhookObj = jsonObject.get("webhook");
144+
String webhook = webhookObj == null ? "" : webhookObj.toString();
145+
if (StringUtils.isBlank(webhook)) {
146+
throw new FormException(Messages.form_validation_webhook(), "robotConfigs[" + i + "].webhook");
147+
}
148+
}
149+
json.put("robotConfigs", robotConfigs);
145150
}
146151

147152
req.bindJSON(this, json);

src/main/java/io/jenkins/plugins/lark/notice/config/LarkRobotConfig.java

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@
2323
import lombok.NoArgsConstructor;
2424
import lombok.Setter;
2525
import lombok.ToString;
26+
import jakarta.servlet.ServletException;
27+
import net.sf.json.JSONObject;
2628
import org.apache.commons.lang3.StringUtils;
2729
import org.kohsuke.stapler.DataBoundConstructor;
30+
import org.kohsuke.stapler.HttpResponse;
2831
import org.kohsuke.stapler.QueryParameter;
32+
import org.kohsuke.stapler.StaplerRequest2;
33+
import org.kohsuke.stapler.StaplerResponse2;
2934
import org.kohsuke.stapler.interceptor.RequirePOST;
3035

36+
import java.io.IOException;
3137
import java.net.ProxySelector;
3238
import java.util.*;
3339
import java.util.stream.Collectors;
@@ -207,32 +213,61 @@ public FormValidation doCheckWebhook(@QueryParameter String value) {
207213
* @param webhook The Webhook key.
208214
* @param proxy The proxy settings.
209215
* @param securityConfigs The security configs.
210-
* @return Returns the test result. If the test passes, it returns FormValidation.respond(Kind.OK); otherwise, it returns an error message.
216+
* @return Test result JSON with keys: ok(boolean), message(string).
211217
*/
212218
@RequirePOST
213-
public String doTest(@QueryParameter String id, @QueryParameter String name,
214-
@QueryParameter String webhook, @QueryParameter String proxy,
215-
@QueryParameter String securityConfigs) {
219+
public HttpResponse doTest(@QueryParameter String id, @QueryParameter String name,
220+
@QueryParameter String webhook, @QueryParameter String proxy,
221+
@QueryParameter String securityConfigs) {
216222
// Check configuration permission
217223
Jenkins.get().checkPermission(LarkPermissions.CONFIGURE);
218224

219-
List<LarkSecurityPolicyConfig> securityPolicyConfigs = JsonUtils.readList(securityConfigs, LarkSecurityPolicyConfig.class)
220-
.stream().filter(config -> StringUtils.isNotBlank(config.getValue())).toList();
225+
JSONObject response = new JSONObject();
226+
try {
227+
List<LarkSecurityPolicyConfig> securityPolicyConfigs = JsonUtils.readList(securityConfigs, LarkSecurityPolicyConfig.class)
228+
.stream().filter(config -> StringUtils.isNotBlank(config.getValue())).toList();
221229

222-
LarkRobotConfig robotConfig = new LarkRobotConfig(id, name, webhook, securityPolicyConfigs);
230+
LarkRobotConfig robotConfig = new LarkRobotConfig(id, name, webhook, securityPolicyConfigs);
223231

224-
ProxySelector proxySelector = Optional.ofNullable(JsonUtils.readValue(proxy, LarkProxyConfig.class))
225-
.orElseGet(LarkProxyConfig::new).obtainProxySelector();
232+
ProxySelector proxySelector = Optional.ofNullable(JsonUtils.readValue(proxy, LarkProxyConfig.class))
233+
.orElseGet(LarkProxyConfig::new).obtainProxySelector();
226234

227-
RobotType robotType = robotConfig.obtainRobotType();
228-
if (Objects.isNull(robotType)) {
229-
return "Error: " + Messages.form_validation_webhook();
230-
}
235+
RobotType robotType = robotConfig.obtainRobotType();
236+
if (Objects.isNull(robotType)) {
237+
response.put("ok", false);
238+
response.put("message", Messages.form_validation_webhook());
239+
return jsonResponse(response);
240+
}
231241

232-
MessageSender sender = robotType.obtainInstance(RobotConfigModel.of(robotConfig, proxySelector));
233-
SendResult sendResult = sender.sendCard(buildTestMessage(robotType));
242+
MessageSender sender = robotType.obtainInstance(RobotConfigModel.of(robotConfig, proxySelector));
243+
SendResult sendResult = sender.sendCard(buildTestMessage(robotType));
244+
boolean ok = sendResult != null && sendResult.isOk();
245+
String detail = sendResult == null ? null : sendResult.getMsg();
246+
response.put("ok", ok);
247+
response.put("message", ok
248+
? Messages.form_validation_test_success()
249+
: StringUtils.isNotBlank(detail)
250+
? Messages.form_validation_test_failed_with_detail(detail)
251+
: Messages.form_validation_test_failed());
252+
return jsonResponse(response);
253+
} catch (Exception e) {
254+
String detail = StringUtils.defaultIfBlank(e.getMessage(), null);
255+
response.put("ok", false);
256+
response.put("message", StringUtils.isNotBlank(detail)
257+
? Messages.form_validation_test_failed_with_detail(detail)
258+
: Messages.form_validation_test_failed());
259+
return jsonResponse(response);
260+
}
261+
}
234262

235-
return sendResult.isOk() ? Messages.form_validation_test_success() : "Error: " + sendResult.getMsg();
263+
private HttpResponse jsonResponse(JSONObject response) {
264+
return new HttpResponse() {
265+
@Override
266+
public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException, ServletException {
267+
rsp.setContentType("application/json; charset=UTF-8");
268+
rsp.getWriter().write(response.toString());
269+
}
270+
};
236271
}
237272

238273
/**

src/main/resources/io/jenkins/plugins/lark/notice/Messages.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ security.policy.type.secret=Signature key
2626
# Robot config form validation
2727
form.validation.name=Name cannot be empty
2828
form.validation.webhook=Invalid Webhook
29-
form.validation.test.success=Test success
29+
form.validation.test.success=Test message sent successfully.
30+
form.validation.test.failed=Failed to send test message. Please check webhook, proxy, and security settings.
31+
form.validation.test.failed.with.detail=Failed to send test message: {0}
3032
form.validation.permission=You do not have permission to access this resource
3133
# Robot test content
3234
robot.test.project.name=Lark Notice Plugin

src/main/resources/io/jenkins/plugins/lark/notice/Messages_zh_CN.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ security.policy.type.secret=\u52A0\u7B7E\u5BC6\u94A5
2626
# \u8868\u5355\u9A8C\u8BC1
2727
form.validation.name=\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
2828
form.validation.webhook=\u65E0\u6548\u7684 Webhook
29-
form.validation.test.success=\u6D4B\u8BD5\u6210\u529F
29+
form.validation.test.success=\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u6210\u529F
30+
form.validation.test.failed=\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 Webhook\u3001\u4EE3\u7406\u4E0E\u5B89\u5168\u7B56\u7565\u914D\u7F6E
31+
form.validation.test.failed.with.detail=\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF1A{0}
3032
form.validation.permission=\u60A8\u6CA1\u6709\u8BBF\u95EE\u6B64\u8D44\u6E90\u7684\u6743\u9650
3133
# \u673A\u5668\u4EBA\u6D4B\u8BD5\u5185\u5BB9
3234
robot.test.project.name=Lark \u901A\u77E5\u63D2\u4EF6

src/main/resources/io/jenkins/plugins/lark/notice/config/LarkGlobalConfig/config.jelly

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
<?jelly escape-by-default='true'?>
22
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler"
33
xmlns:f="/lib/form">
4+
<st:once>
5+
<style>
6+
.lark-bots-repeatable .repeated-container > .repeatable-add {
7+
margin-top: 6px;
8+
margin-bottom: 10px;
9+
}
10+
11+
.lark-bots-repeatable .repeated-container > .repeatable-add-top {
12+
margin-bottom: 10px;
13+
}
14+
</style>
15+
</st:once>
16+
417
<f:section title="${%section.notification.triggers}">
518
<f:entry>
619
<j:scope>
@@ -39,14 +52,16 @@
3952
<j:set var="robotConfigs" value="${descriptor.robotConfigs}"/>
4053
<j:set var="robotConfigDescriptor"
4154
value="${descriptor.getLarkRobotConfigDescriptor()}"/>
42-
<f:repeatable
43-
enableTopButton="true"
44-
field="robotConfigs"
45-
var="item"
46-
items="robotConfigs">
47-
<st:include from="${robotConfigDescriptor}" page="${descriptor.configPage}"/>
48-
<f:repeatableDeleteButton/>
49-
</f:repeatable>
55+
<div class="lark-bots-repeatable">
56+
<f:repeatable
57+
enableTopButton="true"
58+
field="robotConfigs"
59+
var="item"
60+
items="robotConfigs">
61+
<st:include from="${robotConfigDescriptor}" page="${descriptor.configPage}"/>
62+
<f:repeatableDeleteButton/>
63+
</f:repeatable>
64+
</div>
5065
</j:scope>
5166
</f:section>
5267

src/main/resources/io/jenkins/plugins/lark/notice/config/LarkRobotConfig/config.jelly

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,55 @@
55
<st:once>
66
<script type="text/javascript"
77
src="${rootURL}/plugin/lark-notice/scripts/robotConfigValidator.js"/>
8+
<style>
9+
.robot-config-container {
10+
border: 0;
11+
border-radius: 0;
12+
padding: 4px 0 2px;
13+
background: transparent;
14+
}
15+
16+
.robot-config-container .jenkins-form-item {
17+
margin-bottom: 4px;
18+
}
19+
20+
.robot-config-validate-msg {
21+
margin-top: 2px;
22+
font-size: 11px;
23+
line-height: 1.25;
24+
}
25+
26+
.robot-config-validate-msg:empty {
27+
display: none;
28+
}
29+
30+
.robot-config-validate-msg.jenkins-alert {
31+
margin-bottom: 0;
32+
padding: 3px 6px;
33+
}
34+
35+
.robot-config-actions {
36+
margin-top: 0;
37+
text-align: right;
38+
}
39+
40+
.robot-config-container .robot-config-compact-entry {
41+
margin-bottom: 0;
42+
}
43+
</style>
844
</st:once>
945

1046
<div class="robot-config-container">
11-
<f:entry title="${%field.id}" field="id">
12-
<f:textbox name="id"/>
47+
<f:entry title="${%field.id}" field="id"
48+
help="/descriptor/io.jenkins.plugins.lark.notice.config.LarkRobotConfig/help/id">
49+
<f:textbox name="id" readonly="true"/>
1350
</f:entry>
14-
<f:entry title="${%field.name}" field="name">
51+
<f:entry title="${%field.name}" field="name"
52+
help="/descriptor/io.jenkins.plugins.lark.notice.config.LarkRobotConfig/help/name">
1553
<f:textbox name="name"/>
1654
</f:entry>
17-
<f:entry title="${%field.webhook}" field="webhook">
55+
<f:entry title="${%field.webhook}" field="webhook"
56+
help="/descriptor/io.jenkins.plugins.lark.notice.config.LarkRobotConfig/help/webhook">
1857
<f:textbox name="webhook"/>
1958
</f:entry>
2059

@@ -36,18 +75,22 @@
3675
</j:scope>
3776

3877
<st:adjunct includes="io.jenkins.plugins.lark.notice.config.LarkRobotConfig.validate-robot-config-onclick"/>
78+
<f:entry class="robot-config-compact-entry">
79+
<div class="robot-config-actions">
80+
<button
81+
type="button"
82+
class="jenkins-button robot-config-validate-btn"
83+
data-validate-button-descriptor-url="${descriptor.descriptorFullUrl}"
84+
data-validate-button-method="test"
85+
data-validate-button-with="${with}"
86+
data-validate-request-failed-message="${%message.validate.request.failed}"
87+
data-validate-generic-success-message="${%message.validate.success}"
88+
>
89+
${%button.test}
90+
</button>
91+
</div>
92+
</f:entry>
3993
<div class="robot-config-validate-msg"
4094
style="white-space: pre-wrap;"><!-- this is where the error message goes --></div>
41-
<div style="text-align: right;">
42-
<button
43-
type="button"
44-
class="jenkins-button robot-config-validate-btn"
45-
data-validate-button-descriptor-url="${descriptor.descriptorFullUrl}"
46-
data-validate-button-method="test"
47-
data-validate-button-with="${with}"
48-
>
49-
${%button.test}
50-
</button>
51-
</div>
5295
</div>
5396
</j:jelly>

src/main/resources/io/jenkins/plugins/lark/notice/config/LarkRobotConfig/config.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ field.id=ID
22
field.name=Name
33
field.webhook=Webhook
44
field.security.settings=Security Settings
5-
button.test=Test
5+
button.test=Send Test Message
6+
message.validate.request.failed=Unable to run the test request. Please check network, permissions, and Jenkins logs.
7+
message.validate.success=Test completed successfully.

src/main/resources/io/jenkins/plugins/lark/notice/config/LarkRobotConfig/config_zh_CN.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ field.id=ID
22
field.name=\u540D\u79F0
33
field.webhook=Webhook
44
field.security.settings=\u5B89\u5168\u8BBE\u7F6E
5-
button.test=\u6D4B\u8BD5
5+
button.test=\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F
6+
message.validate.request.failed=\u65E0\u6CD5\u6267\u884C\u6D4B\u8BD5\u8BF7\u6C42\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u3001\u6743\u9650\u4E0E Jenkins \u65E5\u5FD7\u3002
7+
message.validate.success=\u6D4B\u8BD5\u6267\u884C\u6210\u529F\u3002
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<div>
2-
Robot id, which can be used in pipeline; If you leave blank, it will be automatically generated. Manually fill in
3-
the blank to ensure uniqueness
4-
</div>
2+
Internal robot identifier used by Pipeline and job-level notifier settings.
3+
</div>
4+
<div>
5+
This value is auto-generated and read-only on the UI.
6+
</div>
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
<div>
2-
机器人 id,可以在 pipeline 中使用;留空会自动生成,手动填写请保证唯一性
3-
</div>
2+
机器人内部标识,可在 Pipeline 与任务级通知配置中引用。
3+
</div>
4+
<div>
5+
该值由系统自动生成,页面中为只读。
6+
</div>

0 commit comments

Comments
 (0)