close https://github.com/go-gitea/gitea/issues/16321 Provided a webhook trigger for requesting someone to review the Pull Request. Some modifications have been made to the returned `PullRequestPayload` based on the GitHub webhook settings, including: - add a description of the current reviewer object as `RequestedReviewer` . - setting the action to either **review_requested** or **review_request_removed** based on the operation. - adding the `RequestedReviewers` field to the issues_model.PullRequest. This field will be loaded into the PullRequest through `LoadRequestedReviewers()` when `ToAPIPullRequest` is called. After the Pull Request is merged, I will supplement the relevant documentation.tags/v1.20.0-rc0
ChangedProtectedFiles []string `xorm:"TEXT JSON"` | ChangedProtectedFiles []string `xorm:"TEXT JSON"` | ||||
IssueID int64 `xorm:"INDEX"` | |||||
Issue *Issue `xorm:"-"` | |||||
Index int64 | |||||
IssueID int64 `xorm:"INDEX"` | |||||
Issue *Issue `xorm:"-"` | |||||
Index int64 | |||||
RequestedReviewers []*user_model.User `xorm:"-"` | |||||
HeadRepoID int64 `xorm:"INDEX"` | HeadRepoID int64 `xorm:"INDEX"` | ||||
HeadRepo *repo_model.Repository `xorm:"-"` | HeadRepo *repo_model.Repository `xorm:"-"` | ||||
return nil | return nil | ||||
} | } | ||||
// LoadRequestedReviewers loads the requested reviewers. | |||||
func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error { | |||||
if len(pr.RequestedReviewers) > 0 { | |||||
return nil | |||||
} | |||||
reviews, err := GetReviewsByIssueID(pr.Issue.ID) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if len(reviews) > 0 { | |||||
err = LoadReviewers(ctx, reviews) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
for _, review := range reviews { | |||||
pr.RequestedReviewers = append(pr.RequestedReviewers, review.Reviewer) | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
// LoadBaseRepo loads the target repository. ErrRepoNotExist may be returned. | // LoadBaseRepo loads the target repository. ErrRepoNotExist may be returned. | ||||
func (pr *PullRequest) LoadBaseRepo(ctx context.Context) (err error) { | func (pr *PullRequest) LoadBaseRepo(ctx context.Context) (err error) { | ||||
if pr.BaseRepo != nil { | if pr.BaseRepo != nil { |
"code.gitea.io/gitea/models/db" | "code.gitea.io/gitea/models/db" | ||||
issues_model "code.gitea.io/gitea/models/issues" | issues_model "code.gitea.io/gitea/models/issues" | ||||
"code.gitea.io/gitea/models/unittest" | "code.gitea.io/gitea/models/unittest" | ||||
user_model "code.gitea.io/gitea/models/user" | |||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
) | ) | ||||
} | } | ||||
} | } | ||||
func TestLoadRequestedReviewers(t *testing.T) { | |||||
assert.NoError(t, unittest.PrepareTestDatabase()) | |||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) | |||||
assert.NoError(t, pull.LoadIssue(db.DefaultContext)) | |||||
issue := pull.Issue | |||||
assert.NoError(t, issue.LoadRepo(db.DefaultContext)) | |||||
assert.Len(t, pull.RequestedReviewers, 0) | |||||
user1, err := user_model.GetUserByID(db.DefaultContext, 1) | |||||
assert.NoError(t, err) | |||||
comment, err := issues_model.AddReviewRequest(issue, user1, &user_model.User{}) | |||||
assert.NoError(t, err) | |||||
assert.NotNil(t, comment) | |||||
assert.NoError(t, pull.LoadRequestedReviewers(db.DefaultContext)) | |||||
assert.Len(t, pull.RequestedReviewers, 1) | |||||
comment, err = issues_model.RemoveReviewRequest(issue, user1, &user_model.User{}) | |||||
assert.NoError(t, err) | |||||
assert.NotNil(t, comment) | |||||
pull.RequestedReviewers = nil | |||||
assert.NoError(t, pull.LoadRequestedReviewers(db.DefaultContext)) | |||||
assert.Empty(t, pull.RequestedReviewers) | |||||
} | |||||
func TestPullRequestsOldest(t *testing.T) { | func TestPullRequestsOldest(t *testing.T) { | ||||
assert.NoError(t, unittest.PrepareTestDatabase()) | assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{ | prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{ |
return err | return err | ||||
} | } | ||||
// LoadReviewers loads reviewers | |||||
func LoadReviewers(ctx context.Context, reviews []*Review) (err error) { | |||||
reviewerIds := make([]int64, len(reviews)) | |||||
for i := 0; i < len(reviews); i++ { | |||||
reviewerIds[i] = reviews[i].ReviewerID | |||||
} | |||||
reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIds) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
userMap := make(map[int64]*user_model.User, len(reviewers)) | |||||
for _, reviewer := range reviewers { | |||||
userMap[reviewer.ID] = reviewer | |||||
} | |||||
for _, review := range reviews { | |||||
review.Reviewer = userMap[review.ReviewerID] | |||||
} | |||||
return nil | |||||
} | |||||
// LoadReviewerTeam loads reviewer team | // LoadReviewerTeam loads reviewer team | ||||
func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) { | func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) { | ||||
if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil { | if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil { | ||||
return reviews, sess.Find(&reviews) | return reviews, sess.Find(&reviews) | ||||
} | } | ||||
// GetReviewersByIssueID gets the latest review of each reviewer for a pull request | |||||
func GetReviewersByIssueID(issueID int64) ([]*Review, error) { | |||||
// GetReviewsByIssueID gets the latest review of each reviewer for a pull request | |||||
func GetReviewsByIssueID(issueID int64) ([]*Review, error) { | |||||
reviews := make([]*Review, 0, 10) | reviews := make([]*Review, 0, 10) | ||||
sess := db.GetEngine(db.DefaultContext) | sess := db.GetEngine(db.DefaultContext) |
UpdatedUnix: 946684814, | UpdatedUnix: 946684814, | ||||
}) | }) | ||||
allReviews, err := issues_model.GetReviewersByIssueID(issue.ID) | |||||
for _, reviewer := range allReviews { | |||||
assert.NoError(t, reviewer.LoadReviewer(db.DefaultContext)) | |||||
allReviews, err := issues_model.GetReviewsByIssueID(issue.ID) | |||||
assert.NoError(t, err) | |||||
for _, review := range allReviews { | |||||
assert.NoError(t, review.LoadReviewer(db.DefaultContext)) | |||||
} | } | ||||
if assert.Len(t, allReviews, 3) { | |||||
for i, review := range allReviews { | |||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer) | |||||
assert.Equal(t, expectedReviews[i].Type, review.Type) | |||||
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix) | |||||
} | |||||
} | |||||
allReviews, err = issues_model.GetReviewsByIssueID(issue.ID) | |||||
assert.NoError(t, err) | assert.NoError(t, err) | ||||
assert.NoError(t, issues_model.LoadReviewers(db.DefaultContext, allReviews)) | |||||
if assert.Len(t, allReviews, 3) { | if assert.Len(t, allReviews, 3) { | ||||
for i, review := range allReviews { | for i, review := range allReviews { | ||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer) | assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer) |
"code.gitea.io/gitea/modules/auth/openid" | "code.gitea.io/gitea/modules/auth/openid" | ||||
"code.gitea.io/gitea/modules/auth/password/hash" | "code.gitea.io/gitea/modules/auth/password/hash" | ||||
"code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
"code.gitea.io/gitea/modules/container" | |||||
"code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
return u, nil | return u, nil | ||||
} | } | ||||
// GetUserByIDs returns the user objects by given IDs if exists. | |||||
func GetUserByIDs(ctx context.Context, ids []int64) ([]*User, error) { | |||||
users := make([]*User, 0, len(ids)) | |||||
err := db.GetEngine(ctx).In("id", ids). | |||||
Table("user"). | |||||
Find(&users) | |||||
return users, err | |||||
} | |||||
// GetPossibleUserByID returns the user if id > 0 or return system usrs if id < 0 | // GetPossibleUserByID returns the user if id > 0 or return system usrs if id < 0 | ||||
func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) { | func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) { | ||||
switch id { | switch id { | ||||
} | } | ||||
} | } | ||||
// GetPossibleUserByIDs returns the users if id > 0 or return system users if id < 0 | |||||
func GetPossibleUserByIDs(ctx context.Context, ids []int64) ([]*User, error) { | |||||
uniqueIDs := container.SetOf(ids...) | |||||
users := make([]*User, 0, len(ids)) | |||||
_ = uniqueIDs.Remove(0) | |||||
if uniqueIDs.Remove(-1) { | |||||
users = append(users, NewGhostUser()) | |||||
} | |||||
if uniqueIDs.Remove(ActionsUserID) { | |||||
users = append(users, NewActionsUser()) | |||||
} | |||||
res, err := GetUserByIDs(ctx, uniqueIDs.Values()) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
users = append(users, res...) | |||||
return users, nil | |||||
} | |||||
// GetUserByNameCtx returns user by given name. | // GetUserByNameCtx returns user by given name. | ||||
func GetUserByName(ctx context.Context, name string) (*User, error) { | func GetUserByName(ctx context.Context, name string) (*User, error) { | ||||
if len(name) == 0 { | if len(name) == 0 { |
(w.ChooseEvents && w.HookEvents.Package) | (w.ChooseEvents && w.HookEvents.Package) | ||||
} | } | ||||
// HasPullRequestReviewRequestEvent returns true if hook enabled pull request review request event. | |||||
func (w *Webhook) HasPullRequestReviewRequestEvent() bool { | |||||
return w.SendEverything || | |||||
(w.ChooseEvents && w.HookEvents.PullRequestReviewRequest) | |||||
} | |||||
// EventCheckers returns event checkers | // EventCheckers returns event checkers | ||||
func (w *Webhook) EventCheckers() []struct { | func (w *Webhook) EventCheckers() []struct { | ||||
Has func() bool | Has func() bool | ||||
{w.HasRepositoryEvent, webhook_module.HookEventRepository}, | {w.HasRepositoryEvent, webhook_module.HookEventRepository}, | ||||
{w.HasReleaseEvent, webhook_module.HookEventRelease}, | {w.HasReleaseEvent, webhook_module.HookEventRelease}, | ||||
{w.HasPackageEvent, webhook_module.HookEventPackage}, | {w.HasPackageEvent, webhook_module.HookEventPackage}, | ||||
{w.HasPullRequestReviewRequestEvent, webhook_module.HookEventPullRequestReviewRequest}, | |||||
} | } | ||||
} | } | ||||
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone", | "pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone", | ||||
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected", | "pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected", | ||||
"pull_request_review_comment", "pull_request_sync", "wiki", "repository", "release", | "pull_request_review_comment", "pull_request_sync", "wiki", "repository", "release", | ||||
"package", | |||||
"package", "pull_request_review_request", | |||||
}, | }, | ||||
(&Webhook{ | (&Webhook{ | ||||
HookEvent: &webhook_module.HookEvent{SendEverything: true}, | HookEvent: &webhook_module.HookEvent{SendEverything: true}, |
HookIssueDemilestoned HookIssueAction = "demilestoned" | HookIssueDemilestoned HookIssueAction = "demilestoned" | ||||
// HookIssueReviewed is an issue action for when a pull request is reviewed | // HookIssueReviewed is an issue action for when a pull request is reviewed | ||||
HookIssueReviewed HookIssueAction = "reviewed" | HookIssueReviewed HookIssueAction = "reviewed" | ||||
// HookIssueReviewRequested is an issue action for when a reviewer is requested for a pull request. | |||||
HookIssueReviewRequested HookIssueAction = "review_requested" | |||||
// HookIssueReviewRequestRemoved is an issue action for removing a review request to someone on a pull request. | |||||
HookIssueReviewRequestRemoved HookIssueAction = "review_request_removed" | |||||
) | ) | ||||
// IssuePayload represents the payload information that is sent along with an issue event. | // IssuePayload represents the payload information that is sent along with an issue event. | ||||
// PullRequestPayload represents a payload information of pull request event. | // PullRequestPayload represents a payload information of pull request event. | ||||
type PullRequestPayload struct { | type PullRequestPayload struct { | ||||
Action HookIssueAction `json:"action"` | |||||
Index int64 `json:"number"` | |||||
Changes *ChangesPayload `json:"changes,omitempty"` | |||||
PullRequest *PullRequest `json:"pull_request"` | |||||
Repository *Repository `json:"repository"` | |||||
Sender *User `json:"sender"` | |||||
CommitID string `json:"commit_id"` | |||||
Review *ReviewPayload `json:"review"` | |||||
Action HookIssueAction `json:"action"` | |||||
Index int64 `json:"number"` | |||||
Changes *ChangesPayload `json:"changes,omitempty"` | |||||
PullRequest *PullRequest `json:"pull_request"` | |||||
RequestedReviewer *User `json:"requested_reviewer"` | |||||
Repository *Repository `json:"repository"` | |||||
Sender *User `json:"sender"` | |||||
CommitID string `json:"commit_id"` | |||||
Review *ReviewPayload `json:"review"` | |||||
} | } | ||||
// JSONPayload FIXME | // JSONPayload FIXME |
// PullRequest represents a pull request | // PullRequest represents a pull request | ||||
type PullRequest struct { | type PullRequest struct { | ||||
ID int64 `json:"id"` | |||||
URL string `json:"url"` | |||||
Index int64 `json:"number"` | |||||
Poster *User `json:"user"` | |||||
Title string `json:"title"` | |||||
Body string `json:"body"` | |||||
Labels []*Label `json:"labels"` | |||||
Milestone *Milestone `json:"milestone"` | |||||
Assignee *User `json:"assignee"` | |||||
Assignees []*User `json:"assignees"` | |||||
State StateType `json:"state"` | |||||
IsLocked bool `json:"is_locked"` | |||||
Comments int `json:"comments"` | |||||
ID int64 `json:"id"` | |||||
URL string `json:"url"` | |||||
Index int64 `json:"number"` | |||||
Poster *User `json:"user"` | |||||
Title string `json:"title"` | |||||
Body string `json:"body"` | |||||
Labels []*Label `json:"labels"` | |||||
Milestone *Milestone `json:"milestone"` | |||||
Assignee *User `json:"assignee"` | |||||
Assignees []*User `json:"assignees"` | |||||
RequestedReviewers []*User `json:"requested_reviewers"` | |||||
State StateType `json:"state"` | |||||
IsLocked bool `json:"is_locked"` | |||||
Comments int `json:"comments"` | |||||
HTMLURL string `json:"html_url"` | HTMLURL string `json:"html_url"` | ||||
DiffURL string `json:"diff_url"` | DiffURL string `json:"diff_url"` |
// HookEvents is a set of web hook events | // HookEvents is a set of web hook events | ||||
type HookEvents struct { | type HookEvents struct { | ||||
Create bool `json:"create"` | |||||
Delete bool `json:"delete"` | |||||
Fork bool `json:"fork"` | |||||
Issues bool `json:"issues"` | |||||
IssueAssign bool `json:"issue_assign"` | |||||
IssueLabel bool `json:"issue_label"` | |||||
IssueMilestone bool `json:"issue_milestone"` | |||||
IssueComment bool `json:"issue_comment"` | |||||
Push bool `json:"push"` | |||||
PullRequest bool `json:"pull_request"` | |||||
PullRequestAssign bool `json:"pull_request_assign"` | |||||
PullRequestLabel bool `json:"pull_request_label"` | |||||
PullRequestMilestone bool `json:"pull_request_milestone"` | |||||
PullRequestComment bool `json:"pull_request_comment"` | |||||
PullRequestReview bool `json:"pull_request_review"` | |||||
PullRequestSync bool `json:"pull_request_sync"` | |||||
Wiki bool `json:"wiki"` | |||||
Repository bool `json:"repository"` | |||||
Release bool `json:"release"` | |||||
Package bool `json:"package"` | |||||
Create bool `json:"create"` | |||||
Delete bool `json:"delete"` | |||||
Fork bool `json:"fork"` | |||||
Issues bool `json:"issues"` | |||||
IssueAssign bool `json:"issue_assign"` | |||||
IssueLabel bool `json:"issue_label"` | |||||
IssueMilestone bool `json:"issue_milestone"` | |||||
IssueComment bool `json:"issue_comment"` | |||||
Push bool `json:"push"` | |||||
PullRequest bool `json:"pull_request"` | |||||
PullRequestAssign bool `json:"pull_request_assign"` | |||||
PullRequestLabel bool `json:"pull_request_label"` | |||||
PullRequestMilestone bool `json:"pull_request_milestone"` | |||||
PullRequestComment bool `json:"pull_request_comment"` | |||||
PullRequestReview bool `json:"pull_request_review"` | |||||
PullRequestSync bool `json:"pull_request_sync"` | |||||
PullRequestReviewRequest bool `json:"pull_request_review_request"` | |||||
Wiki bool `json:"wiki"` | |||||
Repository bool `json:"repository"` | |||||
Release bool `json:"release"` | |||||
Package bool `json:"package"` | |||||
} | } | ||||
// HookEvent represents events that will delivery hook. | // HookEvent represents events that will delivery hook. |
HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected" | HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected" | ||||
HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment" | HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment" | ||||
HookEventPullRequestSync HookEventType = "pull_request_sync" | HookEventPullRequestSync HookEventType = "pull_request_sync" | ||||
HookEventPullRequestReviewRequest HookEventType = "pull_request_review_request" | |||||
HookEventWiki HookEventType = "wiki" | HookEventWiki HookEventType = "wiki" | ||||
HookEventRepository HookEventType = "repository" | HookEventRepository HookEventType = "repository" | ||||
HookEventRelease HookEventType = "release" | HookEventRelease HookEventType = "release" | ||||
case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone: | case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone: | ||||
return "issues" | return "issues" | ||||
case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone, | case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone, | ||||
HookEventPullRequestSync: | |||||
HookEventPullRequestSync, HookEventPullRequestReviewRequest: | |||||
return "pull_request" | return "pull_request" | ||||
case HookEventIssueComment, HookEventPullRequestComment: | case HookEventIssueComment, HookEventPullRequestComment: | ||||
return "issue_comment" | return "issue_comment" |
settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment. | settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment. | ||||
settings.event_pull_request_sync = Pull Request Synchronized | settings.event_pull_request_sync = Pull Request Synchronized | ||||
settings.event_pull_request_sync_desc = Pull request synchronized. | settings.event_pull_request_sync_desc = Pull request synchronized. | ||||
settings.event_pull_request_review_request = Pull Request Review Requested | |||||
settings.event_pull_request_review_request_desc = Pull request review requested or review request removed. | |||||
settings.event_pull_request_approvals = Pull Request Approvals | settings.event_pull_request_approvals = Pull Request Approvals | ||||
settings.event_pull_request_merge = Pull Request Merge | settings.event_pull_request_merge = Pull Request Merge | ||||
settings.event_package = Package | settings.event_package = Package |
HookEvent: &webhook_module.HookEvent{ | HookEvent: &webhook_module.HookEvent{ | ||||
ChooseEvents: true, | ChooseEvents: true, | ||||
HookEvents: webhook_module.HookEvents{ | HookEvents: webhook_module.HookEvents{ | ||||
Create: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true), | |||||
Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true), | |||||
Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true), | |||||
Issues: issuesHook(form.Events, "issues_only"), | |||||
IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)), | |||||
IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)), | |||||
IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)), | |||||
IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)), | |||||
Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true), | |||||
PullRequest: pullHook(form.Events, "pull_request_only"), | |||||
PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)), | |||||
PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)), | |||||
PullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)), | |||||
PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)), | |||||
PullRequestReview: pullHook(form.Events, "pull_request_review"), | |||||
PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)), | |||||
Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true), | |||||
Repository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true), | |||||
Release: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true), | |||||
Create: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true), | |||||
Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true), | |||||
Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true), | |||||
Issues: issuesHook(form.Events, "issues_only"), | |||||
IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)), | |||||
IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)), | |||||
IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)), | |||||
IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)), | |||||
Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true), | |||||
PullRequest: pullHook(form.Events, "pull_request_only"), | |||||
PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)), | |||||
PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)), | |||||
PullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)), | |||||
PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)), | |||||
PullRequestReview: pullHook(form.Events, "pull_request_review"), | |||||
PullRequestReviewRequest: pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)), | |||||
PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)), | |||||
Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true), | |||||
Repository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true), | |||||
Release: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true), | |||||
}, | }, | ||||
BranchFilter: form.BranchFilter, | BranchFilter: form.BranchFilter, | ||||
}, | }, | ||||
w.PullRequestMilestone = pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)) | w.PullRequestMilestone = pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)) | ||||
w.PullRequestComment = pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)) | w.PullRequestComment = pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)) | ||||
w.PullRequestReview = pullHook(form.Events, "pull_request_review") | w.PullRequestReview = pullHook(form.Events, "pull_request_review") | ||||
w.PullRequestReviewRequest = pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)) | |||||
w.PullRequestSync = pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)) | w.PullRequestSync = pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)) | ||||
if err := w.UpdateEvent(); err != nil { | if err := w.UpdateEvent(); err != nil { |
} | } | ||||
ctx.Data["OriginalReviews"] = originalAuthorReviews | ctx.Data["OriginalReviews"] = originalAuthorReviews | ||||
reviews, err := issues_model.GetReviewersByIssueID(issue.ID) | |||||
reviews, err := issues_model.GetReviewsByIssueID(issue.ID) | |||||
if err != nil { | if err != nil { | ||||
ctx.ServerError("GetReviewersByIssueID", err) | ctx.ServerError("GetReviewersByIssueID", err) | ||||
return | return |
SendEverything: form.SendEverything(), | SendEverything: form.SendEverything(), | ||||
ChooseEvents: form.ChooseEvents(), | ChooseEvents: form.ChooseEvents(), | ||||
HookEvents: webhook_module.HookEvents{ | HookEvents: webhook_module.HookEvents{ | ||||
Create: form.Create, | |||||
Delete: form.Delete, | |||||
Fork: form.Fork, | |||||
Issues: form.Issues, | |||||
IssueAssign: form.IssueAssign, | |||||
IssueLabel: form.IssueLabel, | |||||
IssueMilestone: form.IssueMilestone, | |||||
IssueComment: form.IssueComment, | |||||
Release: form.Release, | |||||
Push: form.Push, | |||||
PullRequest: form.PullRequest, | |||||
PullRequestAssign: form.PullRequestAssign, | |||||
PullRequestLabel: form.PullRequestLabel, | |||||
PullRequestMilestone: form.PullRequestMilestone, | |||||
PullRequestComment: form.PullRequestComment, | |||||
PullRequestReview: form.PullRequestReview, | |||||
PullRequestSync: form.PullRequestSync, | |||||
Wiki: form.Wiki, | |||||
Repository: form.Repository, | |||||
Package: form.Package, | |||||
Create: form.Create, | |||||
Delete: form.Delete, | |||||
Fork: form.Fork, | |||||
Issues: form.Issues, | |||||
IssueAssign: form.IssueAssign, | |||||
IssueLabel: form.IssueLabel, | |||||
IssueMilestone: form.IssueMilestone, | |||||
IssueComment: form.IssueComment, | |||||
Release: form.Release, | |||||
Push: form.Push, | |||||
PullRequest: form.PullRequest, | |||||
PullRequestAssign: form.PullRequestAssign, | |||||
PullRequestLabel: form.PullRequestLabel, | |||||
PullRequestMilestone: form.PullRequestMilestone, | |||||
PullRequestComment: form.PullRequestComment, | |||||
PullRequestReview: form.PullRequestReview, | |||||
PullRequestSync: form.PullRequestSync, | |||||
PullRequestReviewRequest: form.PullRequestReviewRequest, | |||||
Wiki: form.Wiki, | |||||
Repository: form.Repository, | |||||
Package: form.Package, | |||||
}, | }, | ||||
BranchFilter: form.BranchFilter, | BranchFilter: form.BranchFilter, | ||||
} | } |
}, | }, | ||||
} | } | ||||
if err = pr.LoadRequestedReviewers(ctx); err != nil { | |||||
log.Error("LoadRequestedReviewers[%d]: %v", pr.ID, err) | |||||
return nil | |||||
} | |||||
for _, reviewer := range pr.RequestedReviewers { | |||||
apiPullRequest.RequestedReviewers = append(apiPullRequest.RequestedReviewers, ToUser(ctx, reviewer, nil)) | |||||
} | |||||
if pr.Issue.ClosedUnix != 0 { | if pr.Issue.ClosedUnix != 0 { | ||||
apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr() | apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr() | ||||
} | } |
// WebhookForm form for changing web hook | // WebhookForm form for changing web hook | ||||
type WebhookForm struct { | type WebhookForm struct { | ||||
Events string | |||||
Create bool | |||||
Delete bool | |||||
Fork bool | |||||
Issues bool | |||||
IssueAssign bool | |||||
IssueLabel bool | |||||
IssueMilestone bool | |||||
IssueComment bool | |||||
Release bool | |||||
Push bool | |||||
PullRequest bool | |||||
PullRequestAssign bool | |||||
PullRequestLabel bool | |||||
PullRequestMilestone bool | |||||
PullRequestComment bool | |||||
PullRequestReview bool | |||||
PullRequestSync bool | |||||
Wiki bool | |||||
Repository bool | |||||
Package bool | |||||
Active bool | |||||
BranchFilter string `binding:"GlobPattern"` | |||||
AuthorizationHeader string | |||||
Events string | |||||
Create bool | |||||
Delete bool | |||||
Fork bool | |||||
Issues bool | |||||
IssueAssign bool | |||||
IssueLabel bool | |||||
IssueMilestone bool | |||||
IssueComment bool | |||||
Release bool | |||||
Push bool | |||||
PullRequest bool | |||||
PullRequestAssign bool | |||||
PullRequestLabel bool | |||||
PullRequestMilestone bool | |||||
PullRequestComment bool | |||||
PullRequestReview bool | |||||
PullRequestSync bool | |||||
PullRequestReviewRequest bool | |||||
Wiki bool | |||||
Repository bool | |||||
Package bool | |||||
Active bool | |||||
BranchFilter string `binding:"GlobPattern"` | |||||
AuthorizationHeader string | |||||
} | } | ||||
// PushOnly if the hook will be triggered when push | // PushOnly if the hook will be triggered when push |
} | } | ||||
} | } | ||||
func (m *webhookNotifier) NotifyPullReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { | |||||
if !issue.IsPull { | |||||
log.Warn("NotifyPullReviewRequest: issue is not a pull request: %v", issue.ID) | |||||
return | |||||
} | |||||
mode, _ := access_model.AccessLevelUnit(ctx, doer, issue.Repo, unit.TypePullRequests) | |||||
if err := issue.LoadPullRequest(ctx); err != nil { | |||||
log.Error("LoadPullRequest failed: %v", err) | |||||
return | |||||
} | |||||
apiPullRequest := &api.PullRequestPayload{ | |||||
Index: issue.Index, | |||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), | |||||
RequestedReviewer: convert.ToUser(ctx, reviewer, nil), | |||||
Repository: convert.ToRepo(ctx, issue.Repo, mode), | |||||
Sender: convert.ToUser(ctx, doer, nil), | |||||
} | |||||
if isRequest { | |||||
apiPullRequest.Action = api.HookIssueReviewRequested | |||||
} else { | |||||
apiPullRequest.Action = api.HookIssueReviewRequestRemoved | |||||
} | |||||
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequestReviewRequest, apiPullRequest); err != nil { | |||||
log.Error("PrepareWebhooks [review_requested: %v]: %v", isRequest, err) | |||||
return | |||||
} | |||||
} | |||||
func (m *webhookNotifier) NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) { | func (m *webhookNotifier) NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) { | ||||
apiPusher := convert.ToUser(ctx, pusher, nil) | apiPusher := convert.ToUser(ctx, pusher, nil) | ||||
apiRepo := convert.ToRepo(ctx, repo, perm.AccessModeNone) | apiRepo := convert.ToRepo(ctx, repo, perm.AccessModeNone) |
case webhook_module.HookEventPush: | case webhook_module.HookEventPush: | ||||
return s.Push(p.(*api.PushPayload)) | return s.Push(p.(*api.PushPayload)) | ||||
case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestLabel, | case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestLabel, | ||||
webhook_module.HookEventPullRequestMilestone, webhook_module.HookEventPullRequestSync: | |||||
webhook_module.HookEventPullRequestMilestone, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestReviewRequest: | |||||
return s.PullRequest(p.(*api.PullRequestPayload)) | return s.PullRequest(p.(*api.PullRequestPayload)) | ||||
case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment: | case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment: | ||||
return s.Review(p.(*api.PullRequestPayload), event) | return s.Review(p.(*api.PullRequestPayload), event) |
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- Pull Request Review Request --> | |||||
<div class="seven wide column"> | |||||
<div class="field"> | |||||
<div class="ui checkbox"> | |||||
<input name="pull_request_review_request" type="checkbox" tabindex="0" {{if .Webhook.PullRequestReviewRequest}}checked{{end}}> | |||||
<label>{{.locale.Tr "repo.settings.event_pull_request_review_request"}}</label> | |||||
<span class="help">{{.locale.Tr "repo.settings.event_pull_request_review_request_desc"}}</span> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
"type": "string", | "type": "string", | ||||
"x-go-name": "PatchURL" | "x-go-name": "PatchURL" | ||||
}, | }, | ||||
"requested_reviewers": { | |||||
"type": "array", | |||||
"items": { | |||||
"$ref": "#/definitions/User" | |||||
}, | |||||
"x-go-name": "RequestedReviewers" | |||||
}, | |||||
"state": { | "state": { | ||||
"$ref": "#/definitions/StateType" | "$ref": "#/definitions/StateType" | ||||
}, | }, |