aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLanre Adelowo <adelowomailbox@gmail.com>2018-12-27 19:04:30 +0100
committertechknowlogick <hello@techknowlogick.com>2018-12-27 13:04:30 -0500
commit945804f800ac4e81c46bde848616e9d9392a9571 (patch)
treef137ad55d93b3f0a426e8f1e0340d786daee05c3
parent8bb0a6f425f994addc2822a21e0dca60e8c5251a (diff)
downloadgitea-945804f800ac4e81c46bde848616e9d9392a9571.tar.gz
gitea-945804f800ac4e81c46bde848616e9d9392a9571.zip
Webhook for Pull Request approval/rejection (#5027)
-rw-r--r--models/review.go50
-rw-r--r--models/webhook.go22
-rw-r--r--models/webhook_dingtalk.go29
-rw-r--r--models/webhook_discord.go52
-rw-r--r--models/webhook_slack.go33
-rw-r--r--options/locale/locale_en-US.ini2
6 files changed, 168 insertions, 20 deletions
diff --git a/models/review.go b/models/review.go
index 91b6d6dbb2..ea2ccebed5 100644
--- a/models/review.go
+++ b/models/review.go
@@ -9,10 +9,11 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
- "github.com/go-xorm/core"
- "github.com/go-xorm/xorm"
+ api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/builder"
+ "github.com/go-xorm/core"
+ "github.com/go-xorm/xorm"
)
// ReviewType defines the sort of feedback a review gives
@@ -233,6 +234,43 @@ func createReview(e Engine, opts CreateReviewOptions) (*Review, error) {
if _, err := e.Insert(review); err != nil {
return nil, err
}
+
+ var reviewHookType HookEventType
+
+ switch opts.Type {
+ case ReviewTypeApprove:
+ reviewHookType = HookEventPullRequestApproved
+ case ReviewTypeComment:
+ reviewHookType = HookEventPullRequestComment
+ case ReviewTypeReject:
+ reviewHookType = HookEventPullRequestRejected
+ default:
+ // unsupported review webhook type here
+ return review, nil
+ }
+
+ pr := opts.Issue.PullRequest
+
+ if err := pr.LoadIssue(); err != nil {
+ return nil, err
+ }
+
+ mode, err := AccessLevel(opts.Issue.Poster, opts.Issue.Repo)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := PrepareWebhooks(opts.Issue.Repo, reviewHookType, &api.PullRequestPayload{
+ Action: api.HookIssueSynchronized,
+ Index: opts.Issue.Index,
+ PullRequest: pr.APIFormat(),
+ Repository: opts.Issue.Repo.APIFormat(mode),
+ Sender: opts.Reviewer.APIFormat(),
+ }); err != nil {
+ return nil, err
+ }
+ go HookQueue.Add(opts.Issue.Repo.ID)
+
return review, nil
}
@@ -285,10 +323,10 @@ type PullReviewersWithType struct {
func GetReviewersByPullID(pullID int64) (issueReviewers []*PullReviewersWithType, err error) {
irs := []*PullReviewersWithType{}
if x.Dialect().DBType() == core.MSSQL {
- err = x.SQL(`SELECT [user].*, review.type, review.review_updated_unix FROM
-(SELECT review.id, review.type, review.reviewer_id, max(review.updated_unix) as review_updated_unix
-FROM review WHERE review.issue_id=? AND (review.type = ? OR review.type = ?)
-GROUP BY review.id, review.type, review.reviewer_id) as review
+ err = x.SQL(`SELECT [user].*, review.type, review.review_updated_unix FROM
+(SELECT review.id, review.type, review.reviewer_id, max(review.updated_unix) as review_updated_unix
+FROM review WHERE review.issue_id=? AND (review.type = ? OR review.type = ?)
+GROUP BY review.id, review.type, review.reviewer_id) as review
INNER JOIN [user] ON review.reviewer_id = [user].id ORDER BY review_updated_unix DESC`,
pullID, ReviewTypeApprove, ReviewTypeReject).
Find(&irs)
diff --git a/models/webhook.go b/models/webhook.go
index 77662f5275..a764455f5f 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -19,7 +19,6 @@ import (
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
-
"github.com/Unknwon/com"
gouuid "github.com/satori/go.uuid"
)
@@ -425,15 +424,18 @@ type HookEventType string
// Types of hook events
const (
- HookEventCreate HookEventType = "create"
- HookEventDelete HookEventType = "delete"
- HookEventFork HookEventType = "fork"
- HookEventPush HookEventType = "push"
- HookEventIssues HookEventType = "issues"
- HookEventIssueComment HookEventType = "issue_comment"
- HookEventPullRequest HookEventType = "pull_request"
- HookEventRepository HookEventType = "repository"
- HookEventRelease HookEventType = "release"
+ HookEventCreate HookEventType = "create"
+ HookEventDelete HookEventType = "delete"
+ HookEventFork HookEventType = "fork"
+ HookEventPush HookEventType = "push"
+ HookEventIssues HookEventType = "issues"
+ HookEventIssueComment HookEventType = "issue_comment"
+ HookEventPullRequest HookEventType = "pull_request"
+ HookEventRepository HookEventType = "repository"
+ HookEventRelease HookEventType = "release"
+ HookEventPullRequestApproved HookEventType = "pull_request_approved"
+ HookEventPullRequestRejected HookEventType = "pull_request_rejected"
+ HookEventPullRequestComment HookEventType = "pull_request_comment"
)
// HookRequest represents hook task request information.
diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go
index 06388a6ba2..dbbbebcd9a 100644
--- a/models/webhook_dingtalk.go
+++ b/models/webhook_dingtalk.go
@@ -11,7 +11,6 @@ import (
"code.gitea.io/git"
api "code.gitea.io/sdk/gitea"
-
dingtalk "github.com/lunny/dingtalk_webhook"
)
@@ -271,6 +270,32 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload,
}, nil
}
+func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*DingtalkPayload, error) {
+ var text, title string
+ switch p.Action {
+ case api.HookIssueSynchronized:
+ action, err := parseHookPullRequestEventType(event)
+ if err != nil {
+ return nil, err
+ }
+
+ title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
+ text = p.PullRequest.Body
+
+ }
+
+ return &DingtalkPayload{
+ MsgType: "actionCard",
+ ActionCard: dingtalk.ActionCard{
+ Text: title + "\r\n\r\n" + text,
+ Title: title,
+ HideAvatar: "0",
+ SingleTitle: "view pull request",
+ SingleURL: p.PullRequest.HTMLURL,
+ },
+ }, nil
+}
+
func getDingtalkRepositoryPayload(p *api.RepositoryPayload) (*DingtalkPayload, error) {
var title, url string
switch p.Action {
@@ -369,6 +394,8 @@ func GetDingtalkPayload(p api.Payloader, event HookEventType, meta string) (*Din
return getDingtalkPushPayload(p.(*api.PushPayload))
case HookEventPullRequest:
return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
+ case HookEventPullRequestApproved, HookEventPullRequestRejected, HookEventPullRequestComment:
+ return getDingtalkPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
case HookEventRepository:
return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload))
case HookEventRelease:
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
index 77634fbe9a..4011880ea9 100644
--- a/models/webhook_discord.go
+++ b/models/webhook_discord.go
@@ -400,6 +400,40 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
}, nil
}
+func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *DiscordMeta, event HookEventType) (*DiscordPayload, error) {
+ var text, title string
+ var color int
+ switch p.Action {
+ case api.HookIssueSynchronized:
+ action, err := parseHookPullRequestEventType(event)
+ if err != nil {
+ return nil, err
+ }
+
+ title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
+ text = p.PullRequest.Body
+ color = warnColor
+ }
+
+ return &DiscordPayload{
+ Username: meta.Username,
+ AvatarURL: meta.IconURL,
+ Embeds: []DiscordEmbed{
+ {
+ Title: title,
+ Description: text,
+ URL: p.PullRequest.HTMLURL,
+ Color: color,
+ Author: DiscordEmbedAuthor{
+ Name: p.Sender.UserName,
+ URL: setting.AppURL + p.Sender.UserName,
+ IconURL: p.Sender.AvatarURL,
+ },
+ },
+ },
+ }, nil
+}
+
func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
var title, url string
var color int
@@ -492,6 +526,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
return getDiscordPushPayload(p.(*api.PushPayload), discord)
case HookEventPullRequest:
return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
+ case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
+ return getDiscordPullRequestApprovalPayload(p.(*api.PullRequestPayload), discord, event)
case HookEventRepository:
return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
case HookEventRelease:
@@ -500,3 +536,19 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
return s, nil
}
+
+func parseHookPullRequestEventType(event HookEventType) (string, error) {
+
+ switch event {
+
+ case HookEventPullRequestApproved:
+ return "approved", nil
+ case HookEventPullRequestRejected:
+ return "rejected", nil
+ case HookEventPullRequestComment:
+ return "comment", nil
+
+ default:
+ return "", errors.New("unknown event type")
+ }
+}
diff --git a/models/webhook_slack.go b/models/webhook_slack.go
index 23df17bf2c..3a21f98601 100644
--- a/models/webhook_slack.go
+++ b/models/webhook_slack.go
@@ -11,9 +11,8 @@ import (
"strings"
"code.gitea.io/git"
- api "code.gitea.io/sdk/gitea"
-
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/sdk/gitea"
)
// SlackMeta contains the slack metadata
@@ -328,6 +327,34 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
}, nil
}
+func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
+ senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+ titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
+ fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
+ var text, title, attachmentText string
+ switch p.Action {
+ case api.HookIssueSynchronized:
+ action, err := parseHookPullRequestEventType(event)
+ if err != nil {
+ return nil, err
+ }
+
+ text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
+ }
+
+ return &SlackPayload{
+ Channel: slack.Channel,
+ Text: text,
+ Username: slack.Username,
+ IconURL: slack.IconURL,
+ Attachments: []SlackAttachment{{
+ Color: slack.Color,
+ Title: title,
+ Text: attachmentText,
+ }},
+ }, nil
+}
+
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
var text, title, attachmentText string
@@ -376,6 +403,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
return getSlackPushPayload(p.(*api.PushPayload), slack)
case HookEventPullRequest:
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
+ case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
+ return getSlackPullRequestApprovalPayload(p.(*api.PullRequestPayload), slack, event)
case HookEventRepository:
return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
case HookEventRelease:
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 5013e0a94b..0835175f16 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1101,7 +1101,7 @@ settings.event_issue_comment_desc = Issue comment created, edited, or deleted.
settings.event_release = Release
settings.event_release_desc = Release published, updated or deleted in a repository.
settings.event_pull_request = Pull Request
-settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared or synchronized.
+settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, approved, rejected, review comment, assigned, unassigned, label updated, label cleared or synchronized.
settings.event_push = Push
settings.event_push_desc = Git push to a repository.
settings.event_repository = Repository