diff options
Diffstat (limited to 'models')
-rw-r--r-- | models/comment.go | 69 | ||||
-rw-r--r-- | models/issue.go | 12 | ||||
-rw-r--r-- | models/milestone.go | 2 | ||||
-rw-r--r-- | models/pull.go | 1 | ||||
-rw-r--r-- | models/webhook.go | 73 | ||||
-rw-r--r-- | models/webhook_discord.go | 41 | ||||
-rw-r--r-- | models/webhook_slack.go | 36 |
7 files changed, 186 insertions, 48 deletions
diff --git a/models/comment.go b/models/comment.go index e166efd9..078be582 100644 --- a/models/comment.go +++ b/models/comment.go @@ -150,9 +150,13 @@ func (c *Comment) APIFormat() *api.Comment { } } +func CommentHashTag(id int64) string { + return "issuecomment-" + com.ToStr(id) +} + // HashTag returns unique hash tag for comment. func (c *Comment) HashTag() string { - return "issuecomment-" + com.ToStr(c.ID) + return CommentHashTag(c.ID) } // EventTag returns unique event hash tag for comment. @@ -330,7 +334,7 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { // CreateIssueComment creates a plain issue comment. func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) { - return CreateComment(&CreateCommentOptions{ + comment, err := CreateComment(&CreateCommentOptions{ Type: COMMENT_TYPE_COMMENT, Doer: doer, Repo: repo, @@ -338,6 +342,22 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri Content: content, Attachments: attachments, }) + if err != nil { + return nil, fmt.Errorf("CreateComment: %v", err) + } + + comment.Issue = issue + if err = PrepareWebhooks(repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{ + Action: api.HOOK_ISSUE_COMMENT_CREATED, + Issue: issue.APIFormat(), + Comment: comment.APIFormat(), + Repository: repo.APIFormat(nil), + Sender: doer.APIFormat(), + }); err != nil { + log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) + } + + return comment, nil } // CreateRefComment creates a commit reference comment to issue. @@ -437,13 +457,33 @@ func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) { } // UpdateComment updates information of comment. -func UpdateComment(c *Comment) error { - _, err := x.Id(c.ID).AllCols().Update(c) - return err +func UpdateComment(doer *User, c *Comment, oldContent string) (err error) { + if _, err = x.Id(c.ID).AllCols().Update(c); err != nil { + return err + } + + if err = c.Issue.LoadAttributes(); err != nil { + log.Error(2, "Issue.LoadAttributes [issue_id: %d]: %v", c.IssueID, err) + } else if err = PrepareWebhooks(c.Issue.Repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{ + Action: api.HOOK_ISSUE_COMMENT_EDITED, + Issue: c.Issue.APIFormat(), + Comment: c.APIFormat(), + Changes: &api.ChangesPayload{ + Body: &api.ChangesFromPayload{ + From: oldContent, + }, + }, + Repository: c.Issue.Repo.APIFormat(nil), + Sender: doer.APIFormat(), + }); err != nil { + log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", c.ID, err) + } + + return nil } // DeleteCommentByID deletes the comment by given ID. -func DeleteCommentByID(id int64) error { +func DeleteCommentByID(doer *User, id int64) error { comment, err := GetCommentByID(id) if err != nil { if IsErrCommentNotExist(err) { @@ -468,5 +508,20 @@ func DeleteCommentByID(id int64) error { } } - return sess.Commit() + if err = sess.Commit(); err != nil { + return fmt.Errorf("Commit: %v", err) + } + + if err = comment.Issue.LoadAttributes(); err != nil { + log.Error(2, "Issue.LoadAttributes [issue_id: %d]: %v", comment.IssueID, err) + } else if err = PrepareWebhooks(comment.Issue.Repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{ + Action: api.HOOK_ISSUE_COMMENT_DELETED, + Issue: comment.Issue.APIFormat(), + Comment: comment.APIFormat(), + Repository: comment.Issue.Repo.APIFormat(nil), + Sender: doer.APIFormat(), + }); err != nil { + log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) + } + return nil } diff --git a/models/issue.go b/models/issue.go index da70b28a..90d57763 100644 --- a/models/issue.go +++ b/models/issue.go @@ -251,8 +251,6 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) { } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) - } else { - go HookQueue.Add(issue.RepoID) } } @@ -363,8 +361,6 @@ func (issue *Issue) ClearLabels(doer *User) (err error) { } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) - } else { - go HookQueue.Add(issue.RepoID) } return nil @@ -502,8 +498,6 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err) - } else { - go HookQueue.Add(repo.ID) } return nil @@ -546,8 +540,6 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) - } else { - go HookQueue.Add(issue.RepoID) } return nil @@ -590,8 +582,6 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) { } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) - } else { - go HookQueue.Add(issue.RepoID) } return nil @@ -641,8 +631,6 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) { } if err != nil { log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err) - } else { - go HookQueue.Add(issue.RepoID) } return nil diff --git a/models/milestone.go b/models/milestone.go index 18c7b696..56a19cd8 100644 --- a/models/milestone.go +++ b/models/milestone.go @@ -348,8 +348,6 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err } if err != nil { log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) - } else { - go HookQueue.Add(issue.RepoID) } return nil diff --git a/models/pull.go b/models/pull.go index d3ea9c78..8e9e0c5d 100644 --- a/models/pull.go +++ b/models/pull.go @@ -454,7 +454,6 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str }); err != nil { log.Error(2, "PrepareWebhooks: %v", err) } - go HookQueue.Add(repo.ID) return nil } diff --git a/models/webhook.go b/models/webhook.go index 2eee5555..32c3ab34 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -62,12 +62,13 @@ func IsValidHookContentType(name string) bool { } type HookEvents struct { - Create bool `json:"create"` - Delete bool `json:"delete"` - Fork bool `json:"fork"` - Push bool `json:"push"` - Issues bool `json:"issues"` - PullRequest bool `json:"pull_request"` + Create bool `json:"create"` + Delete bool `json:"delete"` + Fork bool `json:"fork"` + Push bool `json:"push"` + Issues bool `json:"issues"` + IssueComment bool `json:"issue_comment"` + PullRequest bool `json:"pull_request"` } // HookEvent represents events that will delivery hook. @@ -183,28 +184,38 @@ func (w *Webhook) HasIssuesEvent() bool { (w.ChooseEvents && w.HookEvents.Issues) } +// HasIssueCommentEvent returns true if hook enabled issue comment event. +func (w *Webhook) HasIssueCommentEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.IssueComment) +} + // HasPullRequestEvent returns true if hook enabled pull request event. func (w *Webhook) HasPullRequestEvent() bool { return w.SendEverything || (w.ChooseEvents && w.HookEvents.PullRequest) } +type eventChecker struct { + checker func() bool + typ HookEventType +} + func (w *Webhook) EventsArray() []string { - events := make([]string, 0, 5) - if w.HasCreateEvent() { - events = append(events, string(HOOK_EVENT_CREATE)) - } - if w.HasDeleteEvent() { - events = append(events, string(HOOK_EVENT_DELETE)) - } - if w.HasForkEvent() { - events = append(events, string(HOOK_EVENT_FORK)) + events := make([]string, 0, 7) + eventCheckers := []eventChecker{ + {w.HasCreateEvent, HOOK_EVENT_CREATE}, + {w.HasDeleteEvent, HOOK_EVENT_DELETE}, + {w.HasForkEvent, HOOK_EVENT_FORK}, + {w.HasPushEvent, HOOK_EVENT_PUSH}, + {w.HasIssuesEvent, HOOK_EVENT_ISSUES}, + {w.HasIssueCommentEvent, HOOK_EVENT_ISSUE_COMMENT}, + {w.HasPullRequestEvent, HOOK_EVENT_PULL_REQUEST}, } - if w.HasPushEvent() { - events = append(events, string(HOOK_EVENT_PUSH)) - } - if w.HasPullRequestEvent() { - events = append(events, string(HOOK_EVENT_PULL_REQUEST)) + for _, c := range eventCheckers { + if c.checker() { + events = append(events, string(c.typ)) + } } return events } @@ -363,12 +374,13 @@ func IsValidHookTaskType(name string) bool { type HookEventType string const ( - HOOK_EVENT_CREATE HookEventType = "create" - HOOK_EVENT_DELETE HookEventType = "delete" - HOOK_EVENT_FORK HookEventType = "fork" - HOOK_EVENT_PUSH HookEventType = "push" - HOOK_EVENT_ISSUES HookEventType = "issues" - HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request" + HOOK_EVENT_CREATE HookEventType = "create" + HOOK_EVENT_DELETE HookEventType = "delete" + HOOK_EVENT_FORK HookEventType = "fork" + HOOK_EVENT_PUSH HookEventType = "push" + HOOK_EVENT_ISSUES HookEventType = "issues" + HOOK_EVENT_ISSUE_COMMENT HookEventType = "issue_comment" + HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request" ) // HookRequest represents hook task request information. @@ -496,6 +508,10 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay if !w.HasDeleteEvent() { continue } + case HOOK_EVENT_FORK: + if !w.HasForkEvent() { + continue + } case HOOK_EVENT_PUSH: if !w.HasPushEvent() { continue @@ -504,6 +520,10 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay if !w.HasIssuesEvent() { continue } + case HOOK_EVENT_ISSUE_COMMENT: + if !w.HasIssueCommentEvent() { + continue + } case HOOK_EVENT_PULL_REQUEST: if !w.HasPullRequestEvent() { continue @@ -554,6 +574,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay // It's safe to fail when the whole function is called during hook execution // because resource released after exit. + // FIXME: need a more safe way to not call this function if it's during hook execution. go HookQueue.Add(repo.ID) return nil } diff --git a/models/webhook_discord.go b/models/webhook_discord.go index 48016c13..0e9a36f2 100644 --- a/models/webhook_discord.go +++ b/models/webhook_discord.go @@ -241,6 +241,45 @@ func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) (*DiscordPa }, nil } +func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*DiscordPayload, error) { + title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title) + url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) + content := "" + fields := make([]*DiscordEmbedFieldObject, 0, 1) + switch p.Action { + case api.HOOK_ISSUE_COMMENT_CREATED: + title = "New comment: " + title + content = p.Comment.Body + case api.HOOK_ISSUE_COMMENT_EDITED: + title = "Comment edited: " + title + content = p.Comment.Body + case api.HOOK_ISSUE_COMMENT_DELETED: + title = "Comment deleted: " + title + url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) + content = p.Comment.Body + } + + color, _ := strconv.ParseInt(strings.TrimLeft(slack.Color, "#"), 16, 32) + return &DiscordPayload{ + Username: slack.Username, + AvatarURL: slack.IconURL, + Embeds: []*DiscordEmbedObject{{ + Title: title, + Description: content, + URL: url, + Color: int(color), + Footer: &DiscordEmbedFooterObject{ + Text: p.Repository.FullName, + }, + Author: &DiscordEmbedAuthorObject{ + Name: p.Sender.UserName, + IconURL: p.Sender.AvatarUrl, + }, + Fields: fields, + }}, + }, nil +} + func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*DiscordPayload, error) { title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) @@ -331,6 +370,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (paylo payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack) case HOOK_EVENT_ISSUES: payload, err = getDiscordIssuesPayload(p.(*api.IssuesPayload), slack) + case HOOK_EVENT_ISSUE_COMMENT: + payload, err = getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), slack) case HOOK_EVENT_PULL_REQUEST: payload, err = getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack) } diff --git a/models/webhook_slack.go b/models/webhook_slack.go index 96a44390..d1134ed8 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -191,6 +191,40 @@ func getSlackIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) (*SlackPayloa }, nil } +func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) { + senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName) + titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)), + fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) + var text, title, attachmentText string + switch p.Action { + case api.HOOK_ISSUE_COMMENT_CREATED: + text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink) + title = titleLink + attachmentText = SlackTextFormatter(p.Comment.Body) + case api.HOOK_ISSUE_COMMENT_EDITED: + text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink) + title = titleLink + attachmentText = SlackTextFormatter(p.Comment.Body) + case api.HOOK_ISSUE_COMMENT_DELETED: + text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink) + title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index), + fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) + attachmentText = SlackTextFormatter(p.Comment.Body) + } + + return &SlackPayload{ + Channel: slack.Channel, + Text: text, + Username: slack.Username, + IconURL: slack.IconURL, + Attachments: []*SlackAttachment{{ + Color: slack.Color, + Title: title, + Text: attachmentText, + }}, + }, nil +} + func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName) titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index), @@ -260,6 +294,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload payload, err = getSlackPushPayload(p.(*api.PushPayload), slack) case HOOK_EVENT_ISSUES: payload, err = getSlackIssuesPayload(p.(*api.IssuesPayload), slack) + case HOOK_EVENT_ISSUE_COMMENT: + payload, err = getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack) case HOOK_EVENT_PULL_REQUEST: payload, err = getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack) } |