Browse Source

webhook: add issue comment event

pull/1983/merge
Unknwon 8 years ago
parent
commit
89cc6aa430
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 6
      conf/locale/locale_en-US.ini
  2. 2
      gogs.go
  3. 67
      models/comment.go
  4. 12
      models/issue.go
  5. 2
      models/milestone.go
  6. 1
      models/pull.go
  7. 47
      models/webhook.go
  8. 41
      models/webhook_discord.go
  9. 36
      models/webhook_slack.go
  10. 4
      modules/bindata/bindata.go
  11. 1
      modules/form/repo.go
  12. 5
      routers/api/v1/repo/issue_comment.go
  13. 5
      routers/repo/issue.go
  14. 1
      routers/repo/webhook.go
  15. 2
      templates/.VERSION
  16. 14
      templates/repo/settings/webhook_settings.tmpl
  17. 2
      vendor/github.com/gogits/go-gogs-client/gogs.go
  18. 24
      vendor/github.com/gogits/go-gogs-client/repo_hook.go
  19. 6
      vendor/vendor.json

6
conf/locale/locale_en-US.ini

@ -762,8 +762,10 @@ settings.event_fork = Fork
settings.event_fork_desc = Repository forked
settings.event_push = Push
settings.event_push_desc = Git push to a repository
settings.event_issue = Issues
settings.event_issue_desc = Issue opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, or demilestoned.
settings.event_issues = Issues
settings.event_issues_desc = Issue opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, or demilestoned.
settings.event_issue_comment = Issue Comment
settings.event_issue_comment_desc = Issue comment created, edited, or deleted.
settings.event_pull_request = Pull Request
settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, demilestoned, or synchronized.
settings.active = Active

2
gogs.go

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.10.11.0308"
const APP_VER = "0.10.12.0309"
func init() {
setting.AppVer = APP_VER

67
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)
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
}

12
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

2
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

1
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
}

47
models/webhook.go

@ -67,6 +67,7 @@ type HookEvents struct {
Fork bool `json:"fork"`
Push bool `json:"push"`
Issues bool `json:"issues"`
IssueComment bool `json:"issue_comment"`
PullRequest bool `json:"pull_request"`
}
@ -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)
}
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))
type eventChecker struct {
checker func() bool
typ HookEventType
}
if w.HasForkEvent() {
events = append(events, string(HOOK_EVENT_FORK))
func (w *Webhook) EventsArray() []string {
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))
for _, c := range eventCheckers {
if c.checker() {
events = append(events, string(c.typ))
}
if w.HasPullRequestEvent() {
events = append(events, string(HOOK_EVENT_PULL_REQUEST))
}
return events
}
@ -368,6 +379,7 @@ const (
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"
)
@ -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
}

41
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)
}

36
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)
}

4
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

1
modules/form/repo.go

@ -139,6 +139,7 @@ type Webhook struct {
Fork bool
Push bool
Issues bool
IssueComment bool
PullRequest bool
Active bool
}

5
routers/api/v1/repo/issue_comment.go

@ -92,8 +92,9 @@ func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
return
}
oldContent := comment.Content
comment.Content = form.Body
if err := models.UpdateComment(comment); err != nil {
if err := models.UpdateComment(ctx.User, comment, oldContent); err != nil {
ctx.Error(500, "UpdateComment", err)
return
}
@ -119,7 +120,7 @@ func DeleteIssueComment(ctx *context.APIContext) {
return
}
if err = models.DeleteCommentByID(comment.ID); err != nil {
if err = models.DeleteCommentByID(ctx.User, comment.ID); err != nil {
ctx.Error(500, "DeleteCommentByID", err)
return
}

5
routers/repo/issue.go

@ -906,6 +906,7 @@ func UpdateCommentContent(ctx *context.Context) {
return
}
oldContent := comment.Content
comment.Content = ctx.Query("content")
if len(comment.Content) == 0 {
ctx.JSON(200, map[string]interface{}{
@ -913,7 +914,7 @@ func UpdateCommentContent(ctx *context.Context) {
})
return
}
if err = models.UpdateComment(comment); err != nil {
if err = models.UpdateComment(ctx.User, comment, oldContent); err != nil {
ctx.Handle(500, "UpdateComment", err)
return
}
@ -938,7 +939,7 @@ func DeleteComment(ctx *context.Context) {
return
}
if err = models.DeleteCommentByID(comment.ID); err != nil {
if err = models.DeleteCommentByID(ctx.User, comment.ID); err != nil {
ctx.Handle(500, "DeleteCommentByID", err)
return
}

1
routers/repo/webhook.go

@ -114,6 +114,7 @@ func ParseHookEvent(f form.Webhook) *models.HookEvent {
Fork: f.Fork,
Push: f.Push,
Issues: f.Issues,
IssueComment: f.IssueComment,
PullRequest: f.PullRequest,
},
}

2
templates/.VERSION

@ -1 +1 @@
0.10.11.0308
0.10.12.0309

14
templates/repo/settings/webhook_settings.tmpl

@ -67,8 +67,18 @@
<div class="field">
<div class="ui checkbox">
<input class="hidden" name="issues" type="checkbox" tabindex="0" {{if .Webhook.Issues}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.event_issue"}}</label>
<span class="help">{{.i18n.Tr "repo.settings.event_issue_desc"}}</span>
<label>{{.i18n.Tr "repo.settings.event_issues"}}</label>
<span class="help">{{.i18n.Tr "repo.settings.event_issues_desc"}}</span>
</div>
</div>
</div>
<!-- Issue Comment -->
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
<input class="hidden" name="issue_comment" type="checkbox" tabindex="0" {{if .Webhook.IssueComment}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.event_issue_comment"}}</label>
<span class="help">{{.i18n.Tr "repo.settings.event_issue_comment_desc"}}</span>
</div>
</div>
</div>

2
vendor/github.com/gogits/go-gogs-client/gogs.go generated vendored

@ -14,7 +14,7 @@ import (
)
func Version() string {
return "0.12.8"
return "0.12.9"
}
// Client represents a Gogs API client.

24
vendor/github.com/gogits/go-gogs-client/repo_hook.go generated vendored

@ -92,7 +92,10 @@ type PayloadCommit struct {
var (
_ Payloader = &CreatePayload{}
_ Payloader = &DeletePayload{}
_ Payloader = &ForkPayload{}
_ Payloader = &PushPayload{}
_ Payloader = &IssuesPayload{}
_ Payloader = &IssueCommentPayload{}
_ Payloader = &PullRequestPayload{}
)
@ -266,6 +269,27 @@ func (p *IssuesPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
type HookIssueCommentAction string
const (
HOOK_ISSUE_COMMENT_CREATED HookIssueCommentAction = "created"
HOOK_ISSUE_COMMENT_EDITED HookIssueCommentAction = "edited"
HOOK_ISSUE_COMMENT_DELETED HookIssueCommentAction = "deleted"
)
type IssueCommentPayload struct {
Action HookIssueCommentAction `json:"action"`
Issue *Issue `json:"issue"`
Comment *Comment `json:"comment"`
Changes *ChangesPayload `json:"changes,omitempty"`
Repository *Repository `json:"repository"`
Sender *User `json:"sender"`
}
func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
// __________ .__ .__ __________ __
// \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\

6
vendor/vendor.json vendored

@ -165,10 +165,10 @@
"revisionTime": "2017-02-19T18:16:29Z"
},
{
"checksumSHA1": "Ut3Nu1GaTwTt+Q4k+t9tV3/3nF8=",
"checksumSHA1": "Rvj0LCHGhFQyIM7MzBPt1iRP89c=",
"path": "github.com/gogits/go-gogs-client",
"revision": "559fec59f2c88391ba624da5c40f77c49d8c84eb",
"revisionTime": "2017-03-09T05:00:34Z"
"revision": "8e438478f71b840fcd0b3e810684ea4f6bf476bb",
"revisionTime": "2017-03-09T09:10:09Z"
},
{
"checksumSHA1": "p4yoFWgDiTfpu1JYgh26t6+VDTk=",

Loading…
Cancel
Save