--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
... | ... | @@ -18,7 +18,6 @@ |
18 | 18 |
import models.enumeration.*; |
19 | 19 |
import org.apache.commons.collections.CollectionUtils; |
20 | 20 |
import org.apache.commons.lang.exception.ExceptionUtils; |
21 |
-import org.apache.commons.lang3.BooleanUtils; |
|
22 | 21 |
import org.apache.commons.lang3.StringUtils; |
23 | 22 |
import org.apache.commons.mail.HtmlEmail; |
24 | 23 |
import com.fasterxml.jackson.databind.node.ObjectNode; |
... | ... | @@ -49,7 +48,6 @@ |
49 | 48 |
import views.html.project.transfer; |
50 | 49 |
import views.html.project.change_vcs; |
51 | 50 |
|
52 |
-import javax.annotation.Nonnull; |
|
53 | 51 |
import javax.servlet.ServletException; |
54 | 52 |
import java.io.IOException; |
55 | 53 |
import java.security.NoSuchAlgorithmException; |
... | ... | @@ -1291,22 +1289,27 @@ |
1291 | 1289 |
return notFound(ErrorViews.NotFound.render("error.notfound")); |
1292 | 1290 |
} |
1293 | 1291 |
|
1294 |
- Form<Webhook> addWebhookForm = form(Webhook.class).bindFromRequest(); |
|
1295 |
- if (addWebhookForm == null) { |
|
1292 |
+ Form<Webhook> addNewWebhookForm = form(Webhook.class).bindFromRequest(); |
|
1293 |
+ if (addNewWebhookForm == null) { |
|
1296 | 1294 |
Logger.warn("Failed creating webhook: got null form from newWebhook request"); |
1297 |
- return badRequest(); |
|
1298 |
- } else if (addWebhookForm.hasErrors()) { |
|
1299 |
- return badRequest(ErrorViews.BadRequest.render()); |
|
1295 |
+ return badRequest("Failed creating webhook: got null form from newWebhook request"); |
|
1296 |
+ } else if (addNewWebhookForm.hasErrors()) { |
|
1297 |
+ return badRequest(ErrorViews.BadRequest.render(addNewWebhookForm.errorsAsJson().toString())); |
|
1300 | 1298 |
} |
1301 | 1299 |
|
1302 |
- Webhook webhook = addWebhookForm.get(); |
|
1303 |
- |
|
1304 |
- Webhook.create(project.id, webhook.payloadUrl.trim(), webhook.secret, |
|
1305 |
- BooleanUtils.toBooleanDefaultIfNull(webhook.gitPushOnly, false), webhook.webhookType); |
|
1300 |
+ createWebhook(project, addNewWebhookForm); |
|
1306 | 1301 |
|
1307 | 1302 |
return redirect(routes.ProjectApp.webhooks(project.owner, project.name)); |
1308 | 1303 |
} |
1309 | 1304 |
|
1305 |
+ private static void createWebhook(Project project, Form<Webhook> forms) { |
|
1306 |
+ Webhook webhook = forms.get(); |
|
1307 |
+ webhook.project = project; |
|
1308 |
+ if(webhook.gitPushOnly == null) webhook.gitPushOnly = false; |
|
1309 |
+ webhook.createdAt = new Date(); |
|
1310 |
+ webhook.save(); |
|
1311 |
+ } |
|
1312 |
+ |
|
1310 | 1313 |
@Transactional |
1311 | 1314 |
@IsAllowed(Operation.UPDATE) |
1312 | 1315 |
public static Result deleteWebhook(String ownerId, String projectName, Long id) { |
--- app/models/Webhook.java
+++ app/models/Webhook.java
... | ... | @@ -1,23 +1,10 @@ |
1 | 1 |
/** |
2 |
- * Yobi, Project Hosting SW |
|
3 |
- * |
|
4 |
- * Copyright 2015 NAVER Corp. |
|
5 |
- * http://yobi.io |
|
6 |
- * |
|
7 |
- * @author Jihwan Chun |
|
8 |
- * |
|
9 |
- * Licensed under the Apache License, Version 2.0 (the "License"); |
|
10 |
- * you may not use this file except in compliance with the License. |
|
11 |
- * You may obtain a copy of the License at |
|
12 |
- * |
|
13 |
- * http://www.apache.org/licenses/LICENSE-2.0 |
|
14 |
- * |
|
15 |
- * Unless required by applicable law or agreed to in writing, software |
|
16 |
- * distributed under the License is distributed on an "AS IS" BASIS, |
|
17 |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
18 |
- * See the License for the specific language governing permissions and |
|
19 |
- * limitations under the License. |
|
20 |
- */ |
|
2 |
+ * Yona, 21st Century Project Hosting SW |
|
3 |
+ * <p> |
|
4 |
+ * Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. |
|
5 |
+ * https://yona.io |
|
6 |
+ **/ |
|
7 |
+ |
|
21 | 8 |
package models; |
22 | 9 |
|
23 | 10 |
import com.fasterxml.jackson.databind.ObjectMapper; |
... | ... | @@ -26,6 +13,7 @@ |
26 | 13 |
import models.enumeration.EventType; |
27 | 14 |
import models.enumeration.PullRequestReviewAction; |
28 | 15 |
import models.enumeration.ResourceType; |
16 |
+import models.enumeration.WebhookType; |
|
29 | 17 |
import models.resource.GlobalResource; |
30 | 18 |
import models.resource.Resource; |
31 | 19 |
import models.resource.ResourceConvertible; |
... | ... | @@ -90,10 +78,7 @@ |
90 | 78 |
*/ |
91 | 79 |
public Boolean gitPushOnly; |
92 | 80 |
|
93 |
- /** |
|
94 |
- * Type of sending webhook (0: Simple Ver., 1: Slack Detail) |
|
95 |
- */ |
|
96 |
- public Long webhookType; |
|
81 |
+ public WebhookType webhookType = WebhookType.WITH_DETAILS; |
|
97 | 82 |
|
98 | 83 |
/** |
99 | 84 |
* Payload URL of webhook. |
... | ... | @@ -109,7 +94,7 @@ |
109 | 94 |
* @param gitPushOnly type of webhook (true = git only push, false = all cases) |
110 | 95 |
* @param secret the secret token for server identity |
111 | 96 |
*/ |
112 |
- public Webhook(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly, Long webhookType) { |
|
97 |
+ public Webhook(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly) { |
|
113 | 98 |
if (secret == null) { |
114 | 99 |
secret = ""; |
115 | 100 |
} |
... | ... | @@ -117,7 +102,6 @@ |
117 | 102 |
this.payloadUrl = payloadUrl; |
118 | 103 |
this.secret = secret; |
119 | 104 |
this.gitPushOnly = gitPushOnly; |
120 |
- this.webhookType = webhookType; |
|
121 | 105 |
this.createdAt = new Date(); |
122 | 106 |
} |
123 | 107 |
|
... | ... | @@ -148,9 +132,9 @@ |
148 | 132 |
return find.where().eq("project.id", projectId).findList(); |
149 | 133 |
} |
150 | 134 |
|
151 |
- public static void create(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly, Long webhookType) { |
|
135 |
+ public static void create(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly) { |
|
152 | 136 |
if (!payloadUrl.isEmpty()) { |
153 |
- Webhook webhook = new Webhook(projectId, payloadUrl, secret, gitPushOnly, webhookType); |
|
137 |
+ Webhook webhook = new Webhook(projectId, payloadUrl, secret, gitPushOnly); |
|
154 | 138 |
webhook.save(); |
155 | 139 |
} |
156 | 140 |
// TODO : Raise appropriate error when required field is empty |
... | ... | @@ -266,16 +250,19 @@ |
266 | 250 |
} |
267 | 251 |
requestMessage += " <" + utils.Config.getScheme() + "://" + utils.Config.getHostport("localhost:9000") + RouteUtil.getUrl(eventPullRequest) + "|#" + eventPullRequest.number + ": " + eventPullRequest.title + ">"; |
268 | 252 |
|
269 |
- if (this.webhookType == 1) { |
|
270 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.sender"), eventPullRequest.contributor.name, false)); |
|
271 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.from"), eventPullRequest.fromBranch, true)); |
|
272 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.to"), eventPullRequest.toBranch, true)); |
|
273 |
- attachments.add(buildAttachmentJSON(eventPullRequest.body, detailFields)); |
|
274 |
- return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
|
253 |
+ if (this.webhookType == WebhookType.WITH_DETAILS) { |
|
254 |
+ return buildJsonWithPullReqtuestDetails(eventPullRequest, detailFields, attachments, requestMessage); |
|
275 | 255 |
} else { |
276 |
- return Json.stringify(buildRequestJSON(requestMessage)); |
|
256 |
+ return buildTextPropertyOnlyJSON(requestMessage); |
|
277 | 257 |
} |
258 |
+ } |
|
278 | 259 |
|
260 |
+ private String buildJsonWithPullReqtuestDetails(PullRequest eventPullRequest, ArrayNode detailFields, ArrayNode attachments, String requestMessage) { |
|
261 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.sender"), eventPullRequest.contributor.name, false)); |
|
262 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.from"), eventPullRequest.fromBranch, true)); |
|
263 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.to"), eventPullRequest.toBranch, true)); |
|
264 |
+ attachments.add(buildAttachmentJSON(eventPullRequest.body, detailFields)); |
|
265 |
+ return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
|
279 | 266 |
} |
280 | 267 |
|
281 | 268 |
private String buildRequestBody(EventType eventType, User sender, PullRequest eventPullRequest, PullRequestReviewAction reviewAction) { |
... | ... | @@ -295,14 +282,10 @@ |
295 | 282 |
} |
296 | 283 |
requestMessage += " <" + utils.Config.getScheme() + "://" + utils.Config.getHostport("localhost:9000") + RouteUtil.getUrl(eventPullRequest) + "|#" + eventPullRequest.number + ": " + eventPullRequest.title + ">"; |
297 | 284 |
|
298 |
- if (this.webhookType == 1) { |
|
299 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.sender"), eventPullRequest.contributor.name, false)); |
|
300 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.from"), eventPullRequest.fromBranch, true)); |
|
301 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.to"), eventPullRequest.toBranch, true)); |
|
302 |
- attachments.add(buildAttachmentJSON(eventPullRequest.body, detailFields)); |
|
303 |
- return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
|
285 |
+ if (this.webhookType == WebhookType.SIMPLE) { |
|
286 |
+ return buildTextPropertyOnlyJSON(requestMessage); |
|
304 | 287 |
} else { |
305 |
- return Json.stringify(buildRequestJSON(requestMessage)); |
|
288 |
+ return buildJsonWithPullReqtuestDetails(eventPullRequest, detailFields, attachments, requestMessage); |
|
306 | 289 |
} |
307 | 290 |
} |
308 | 291 |
|
... | ... | @@ -337,15 +320,19 @@ |
337 | 320 |
|
338 | 321 |
requestMessage += " <" + utils.Config.getScheme() + "://" + utils.Config.getHostport("localhost:9000") + RouteUtil.getUrl(eventIssue) + "|#" + eventIssue.number + ": " + eventIssue.title + ">"; |
339 | 322 |
|
340 |
- if (this.webhookType == 1) { |
|
341 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "notification.type.milestone.changed"), eventIssue.milestoneId().toString(), true)); |
|
342 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), ""), eventIssue.assigneeName(), true)); |
|
343 |
- detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "issue.state"), eventIssue.state.toString(), true)); |
|
344 |
- attachments.add(buildAttachmentJSON(eventIssue.body, detailFields)); |
|
345 |
- return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
|
323 |
+ if (this.webhookType == WebhookType.SIMPLE) { |
|
324 |
+ return buildTextPropertyOnlyJSON(requestMessage); |
|
346 | 325 |
} else { |
347 |
- return Json.stringify(buildRequestJSON(requestMessage)); |
|
326 |
+ return buildJsonWithIssueEventDetails(eventIssue, detailFields, attachments, requestMessage); |
|
348 | 327 |
} |
328 |
+ } |
|
329 |
+ |
|
330 |
+ private String buildJsonWithIssueEventDetails(Issue eventIssue, ArrayNode detailFields, ArrayNode attachments, String requestMessage) { |
|
331 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "notification.type.milestone.changed"), eventIssue.milestoneId().toString(), true)); |
|
332 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), ""), eventIssue.assigneeName(), true)); |
|
333 |
+ detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "issue.state"), eventIssue.state.toString(), true)); |
|
334 |
+ attachments.add(buildAttachmentJSON(eventIssue.body, detailFields)); |
|
335 |
+ return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
|
349 | 336 |
} |
350 | 337 |
|
351 | 338 |
private String buildRequestBody(EventType eventType, User sender, Comment eventComment) { |
... | ... | @@ -370,11 +357,11 @@ |
370 | 357 |
break; |
371 | 358 |
} |
372 | 359 |
|
373 |
- if (this.webhookType == 1) { |
|
360 |
+ if (this.webhookType == WebhookType.SIMPLE) { |
|
361 |
+ return buildTextPropertyOnlyJSON(requestMessage); |
|
362 |
+ } else { |
|
374 | 363 |
attachments.add(buildAttachmentJSON(eventComment.contents, null)); |
375 | 364 |
return Json.stringify(buildRequestJSON(requestMessage, attachments)); |
376 |
- } else { |
|
377 |
- return Json.stringify(buildRequestJSON(requestMessage)); |
|
378 | 365 |
} |
379 | 366 |
} |
380 | 367 |
|
... | ... | @@ -428,10 +415,10 @@ |
428 | 415 |
return requestBody; |
429 | 416 |
} |
430 | 417 |
|
431 |
- private ObjectNode buildRequestJSON(String requestMessage) { |
|
418 |
+ private String buildTextPropertyOnlyJSON(String requestMessage) { |
|
432 | 419 |
ObjectNode requestBody = Json.newObject(); |
433 | 420 |
requestBody.put("text", requestMessage); |
434 |
- return requestBody; |
|
421 |
+ return Json.stringify(requestBody); |
|
435 | 422 |
} |
436 | 423 |
|
437 | 424 |
private ObjectNode buildSenderJSON(User sender) { |
+++ app/models/enumeration/WebhookType.java
... | ... | @@ -0,0 +1,17 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * <p> | |
4 | + * Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. | |
5 | + * https://yona.io | |
6 | + **/ | |
7 | +package models.enumeration; | |
8 | + | |
9 | +public enum WebhookType { | |
10 | + SIMPLE(0), WITH_DETAILS(1); | |
11 | + | |
12 | + private int type; | |
13 | + | |
14 | + WebhookType(int type) { | |
15 | + this.type = type; | |
16 | + } | |
17 | +} |
--- app/views/project/partial_webhooks_list.scala.html
+++ app/views/project/partial_webhooks_list.scala.html
... | ... | @@ -30,27 +30,36 @@ |
30 | 30 |
</div> |
31 | 31 |
} else { |
32 | 32 |
<div class="row-fluid list-head"> |
33 |
- <div class="span8 payload-url"> |
|
33 |
+ <div class="span6 payload-url"> |
|
34 | 34 |
<strong>@Messages("project.webhook.payloadUrl")</strong> |
35 | 35 |
</div> |
36 |
- <div class="span4 secret"> |
|
37 |
- <strong>@Messages("project.webhook.secret") / Working only when Git push</strong> |
|
36 |
+ <div class="span6 secret"> |
|
37 |
+ <strong>@Messages("project.webhook.secret") | Include details or not | Working only when to push commit</strong> |
|
38 | 38 |
</div> |
39 | 39 |
</div> |
40 | 40 |
|
41 | 41 |
@webhooks.map { webhook => |
42 | 42 |
<div class="row-fluid list-item" data-webhook-id="@webhook.id"> |
43 |
- <div class="span8"> |
|
43 |
+ <div class="span6"> |
|
44 | 44 |
<h6 class="mr20 truncate"> |
45 | 45 |
@webhook.payloadUrl |
46 | 46 |
</h6> |
47 | 47 |
</div> |
48 |
- <div class="span4"> |
|
48 |
+ <div class="span6"> |
|
49 | 49 |
<table class="table nm"> |
50 | 50 |
<tr> |
51 | 51 |
<td> |
52 | 52 |
<span class="webhook-secret truncate"> |
53 |
- @webhook.secret |
|
53 |
+ @if(webhook.secret.isEmpty) { |
|
54 |
+ NONE |
|
55 |
+ } else { |
|
56 |
+ @webhook.secret |
|
57 |
+ } |
|
58 |
+ </span> |
|
59 |
+ </td> |
|
60 |
+ <td> |
|
61 |
+ <span class="webhook-secret truncate"> |
|
62 |
+ @webhook.webhookType |
|
54 | 63 |
</span> |
55 | 64 |
</td> |
56 | 65 |
<td> |
--- app/views/project/webhooks.scala.html
+++ app/views/project/webhooks.scala.html
... | ... | @@ -22,12 +22,12 @@ |
22 | 22 |
<div> |
23 | 23 |
<input type="text" name="payloadUrl" class="input-webhook-payload" maxlength="2000" autocomplete="off" placeholder="@Messages("project.webhook.payloadUrl")"> |
24 | 24 |
<input type="text" name="secret" class="input-webhook-secret" maxlength="250" autocomplete="off" placeholder="@Messages("project.webhook.secret")"> |
25 |
+ <select class="form-control" title="add details or not" name="webhookType"> |
|
26 |
+ <option value="SIMPLE">SIMPLE Version (no details)</option> |
|
27 |
+ <option value="WITH_DETAILS">Normal (Slack/with details)</option> |
|
28 |
+ </select> |
|
25 | 29 |
<label for="gitPushOnly">@Messages("project.webhook.gitPushOnly")</label> |
26 | 30 |
<input type="checkbox" id="gitPushOnly" name="gitPushOnly" class="form-check-input"> |
27 |
- <select class="form-control" name="WebhookType"> |
|
28 |
- <option value="0">SIMPLE Version</option> |
|
29 |
- <option value="1">Detail (Slack)</option> |
|
30 |
- </select> |
|
31 | 31 |
</div> |
32 | 32 |
</div> |
33 | 33 |
<button type="submit" class="ybtn ybtn-primary btn-submit">@Messages("project.webhook.add")</button> |
+++ conf/evolutions/default/24.sql
... | ... | @@ -0,0 +1,6 @@ |
1 | +# --- !Ups | |
2 | +ALTER TABLE webhook ADD COLUMN webhook_type tinyint(1) default 1; | |
3 | +CREATE INDEX ix_webhook_webhook_type ON webhook (webhook_type); | |
4 | + | |
5 | +# --- !Downs | |
6 | +ALTER TABLE webhook DROP COLUMN webhook_type;(파일 끝에 줄바꿈 문자 없음) |
--- conf/messages
+++ conf/messages
... | ... | @@ -717,7 +717,7 @@ |
717 | 717 |
project.webhook.payloadUrl.tooLong = Given payload URL is too long. (Maximum 2000 Characters) |
718 | 718 |
project.webhook.secret = Authorization Token |
719 | 719 |
project.webhook.secret.tooLong = Given secret token is too long. (Maximum 250 Characters) |
720 |
-project.webhook.gitPushOnly = Git push hook |
|
720 |
+project.webhook.gitPushOnly = When to Git push only |
|
721 | 721 |
project.you.are.not.watching = You are not watching {0} project. |
722 | 722 |
project.you.are.watching = You are watching {0} project. |
723 | 723 |
project.you.may.want.to.be.a.member = You can send a sign-up request for {0} project. |
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
... | ... | @@ -716,7 +716,7 @@ |
716 | 716 |
project.webhook.payloadUrl = 전송할 주소 |
717 | 717 |
project.webhook.payloadUrl.empty = Paylaod URL 은 필수 필드입니다. |
718 | 718 |
project.webhook.payloadUrl.tooLong = Payload URL 이 너무 깁니다 (최대 2000글자)웹후크 추가 |
719 |
-project.webhook.gitPushOnly = Git push hook |
|
719 |
+project.webhook.gitPushOnly = Git push 시에만 동작을 원하면 체크 |
|
720 | 720 |
project.webhook.secret = Authorization Token |
721 | 721 |
project.webhook.secret.tooLong = 보안 토큰이 너무 깁니다. (최대 250 글자) |
722 | 722 |
project.you.are.not.watching = {0} 프로젝트를 지켜보고 있지 않습니다. |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?