mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-20 20:07:03 +01:00
New webhook trigger for receiving Pull Request review requests (#24481)
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.
This commit is contained in:
parent
93c6a9a652
commit
309354c70e
21 changed files with 305 additions and 117 deletions
|
@ -175,9 +175,10 @@ type PullRequest struct {
|
||||||
|
|
||||||
ChangedProtectedFiles []string `xorm:"TEXT JSON"`
|
ChangedProtectedFiles []string `xorm:"TEXT JSON"`
|
||||||
|
|
||||||
IssueID int64 `xorm:"INDEX"`
|
IssueID int64 `xorm:"INDEX"`
|
||||||
Issue *Issue `xorm:"-"`
|
Issue *Issue `xorm:"-"`
|
||||||
Index int64
|
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:"-"`
|
||||||
|
@ -302,6 +303,29 @@ func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) {
|
||||||
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 {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
@ -74,6 +75,34 @@ func TestPullRequestsNewest(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
|
|
|
@ -162,6 +162,27 @@ func (r *Review) LoadReviewer(ctx context.Context) (err error) {
|
||||||
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 {
|
||||||
|
@ -520,8 +541,8 @@ func GetReviews(ctx context.Context, opts *GetReviewOptions) ([]*Review, error)
|
||||||
return reviews, sess.Find(&reviews)
|
return reviews, sess.Find(&reviews)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReviewersByIssueID gets the latest review of each reviewer for a pull request
|
// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
|
||||||
func GetReviewersByIssueID(issueID int64) ([]*Review, error) {
|
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)
|
||||||
|
|
|
@ -132,11 +132,22 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||||
UpdatedUnix: 946684814,
|
UpdatedUnix: 946684814,
|
||||||
})
|
})
|
||||||
|
|
||||||
allReviews, err := issues_model.GetReviewersByIssueID(issue.ID)
|
allReviews, err := issues_model.GetReviewsByIssueID(issue.ID)
|
||||||
for _, reviewer := range allReviews {
|
|
||||||
assert.NoError(t, reviewer.LoadReviewer(db.DefaultContext))
|
|
||||||
}
|
|
||||||
assert.NoError(t, err)
|
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, 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)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"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"
|
||||||
|
@ -910,6 +911,15 @@ func GetUserByID(ctx context.Context, id int64) (*User, error) {
|
||||||
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 {
|
||||||
|
@ -924,6 +934,25 @@ func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
|
@ -298,6 +298,12 @@ func (w *Webhook) HasPackageEvent() bool {
|
||||||
(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
|
||||||
|
@ -329,6 +335,7 @@ func (w *Webhook) EventCheckers() []struct {
|
||||||
{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},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ func TestWebhook_EventsArray(t *testing.T) {
|
||||||
"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},
|
||||||
|
|
|
@ -342,6 +342,10 @@ const (
|
||||||
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.
|
||||||
|
@ -381,14 +385,15 @@ type ChangesPayload struct {
|
||||||
|
|
||||||
// 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"`
|
Action HookIssueAction `json:"action"`
|
||||||
Index int64 `json:"number"`
|
Index int64 `json:"number"`
|
||||||
Changes *ChangesPayload `json:"changes,omitempty"`
|
Changes *ChangesPayload `json:"changes,omitempty"`
|
||||||
PullRequest *PullRequest `json:"pull_request"`
|
PullRequest *PullRequest `json:"pull_request"`
|
||||||
Repository *Repository `json:"repository"`
|
RequestedReviewer *User `json:"requested_reviewer"`
|
||||||
Sender *User `json:"sender"`
|
Repository *Repository `json:"repository"`
|
||||||
CommitID string `json:"commit_id"`
|
Sender *User `json:"sender"`
|
||||||
Review *ReviewPayload `json:"review"`
|
CommitID string `json:"commit_id"`
|
||||||
|
Review *ReviewPayload `json:"review"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONPayload FIXME
|
// JSONPayload FIXME
|
||||||
|
|
|
@ -9,19 +9,20 @@ import (
|
||||||
|
|
||||||
// PullRequest represents a pull request
|
// PullRequest represents a pull request
|
||||||
type PullRequest struct {
|
type PullRequest struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Index int64 `json:"number"`
|
Index int64 `json:"number"`
|
||||||
Poster *User `json:"user"`
|
Poster *User `json:"user"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
Labels []*Label `json:"labels"`
|
Labels []*Label `json:"labels"`
|
||||||
Milestone *Milestone `json:"milestone"`
|
Milestone *Milestone `json:"milestone"`
|
||||||
Assignee *User `json:"assignee"`
|
Assignee *User `json:"assignee"`
|
||||||
Assignees []*User `json:"assignees"`
|
Assignees []*User `json:"assignees"`
|
||||||
State StateType `json:"state"`
|
RequestedReviewers []*User `json:"requested_reviewers"`
|
||||||
IsLocked bool `json:"is_locked"`
|
State StateType `json:"state"`
|
||||||
Comments int `json:"comments"`
|
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"`
|
||||||
|
|
|
@ -5,26 +5,27 @@ package webhook
|
||||||
|
|
||||||
// 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"`
|
Create bool `json:"create"`
|
||||||
Delete bool `json:"delete"`
|
Delete bool `json:"delete"`
|
||||||
Fork bool `json:"fork"`
|
Fork bool `json:"fork"`
|
||||||
Issues bool `json:"issues"`
|
Issues bool `json:"issues"`
|
||||||
IssueAssign bool `json:"issue_assign"`
|
IssueAssign bool `json:"issue_assign"`
|
||||||
IssueLabel bool `json:"issue_label"`
|
IssueLabel bool `json:"issue_label"`
|
||||||
IssueMilestone bool `json:"issue_milestone"`
|
IssueMilestone bool `json:"issue_milestone"`
|
||||||
IssueComment bool `json:"issue_comment"`
|
IssueComment bool `json:"issue_comment"`
|
||||||
Push bool `json:"push"`
|
Push bool `json:"push"`
|
||||||
PullRequest bool `json:"pull_request"`
|
PullRequest bool `json:"pull_request"`
|
||||||
PullRequestAssign bool `json:"pull_request_assign"`
|
PullRequestAssign bool `json:"pull_request_assign"`
|
||||||
PullRequestLabel bool `json:"pull_request_label"`
|
PullRequestLabel bool `json:"pull_request_label"`
|
||||||
PullRequestMilestone bool `json:"pull_request_milestone"`
|
PullRequestMilestone bool `json:"pull_request_milestone"`
|
||||||
PullRequestComment bool `json:"pull_request_comment"`
|
PullRequestComment bool `json:"pull_request_comment"`
|
||||||
PullRequestReview bool `json:"pull_request_review"`
|
PullRequestReview bool `json:"pull_request_review"`
|
||||||
PullRequestSync bool `json:"pull_request_sync"`
|
PullRequestSync bool `json:"pull_request_sync"`
|
||||||
Wiki bool `json:"wiki"`
|
PullRequestReviewRequest bool `json:"pull_request_review_request"`
|
||||||
Repository bool `json:"repository"`
|
Wiki bool `json:"wiki"`
|
||||||
Release bool `json:"release"`
|
Repository bool `json:"repository"`
|
||||||
Package bool `json:"package"`
|
Release bool `json:"release"`
|
||||||
|
Package bool `json:"package"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookEvent represents events that will delivery hook.
|
// HookEvent represents events that will delivery hook.
|
||||||
|
|
|
@ -26,6 +26,7 @@ const (
|
||||||
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"
|
||||||
|
@ -46,7 +47,7 @@ func (h HookEventType) Event() string {
|
||||||
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"
|
||||||
|
|
|
@ -2118,6 +2118,8 @@ settings.event_pull_request_review = Pull Request Reviewed
|
||||||
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
|
||||||
|
|
|
@ -179,25 +179,26 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
|
||||||
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),
|
Create: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true),
|
||||||
Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true),
|
Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true),
|
||||||
Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true),
|
Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true),
|
||||||
Issues: issuesHook(form.Events, "issues_only"),
|
Issues: issuesHook(form.Events, "issues_only"),
|
||||||
IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)),
|
IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)),
|
||||||
IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)),
|
IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)),
|
||||||
IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)),
|
IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)),
|
||||||
IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)),
|
IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)),
|
||||||
Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true),
|
Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true),
|
||||||
PullRequest: pullHook(form.Events, "pull_request_only"),
|
PullRequest: pullHook(form.Events, "pull_request_only"),
|
||||||
PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)),
|
PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)),
|
||||||
PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)),
|
PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)),
|
||||||
PullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)),
|
PullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)),
|
||||||
PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)),
|
PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)),
|
||||||
PullRequestReview: pullHook(form.Events, "pull_request_review"),
|
PullRequestReview: pullHook(form.Events, "pull_request_review"),
|
||||||
PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)),
|
PullRequestReviewRequest: pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)),
|
||||||
Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true),
|
PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)),
|
||||||
Repository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true),
|
Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true),
|
||||||
Release: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), 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,
|
||||||
},
|
},
|
||||||
|
@ -379,6 +380,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
|
||||||
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 {
|
||||||
|
|
|
@ -576,7 +576,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
|
@ -160,26 +160,27 @@ func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
|
||||||
SendEverything: form.SendEverything(),
|
SendEverything: form.SendEverything(),
|
||||||
ChooseEvents: form.ChooseEvents(),
|
ChooseEvents: form.ChooseEvents(),
|
||||||
HookEvents: webhook_module.HookEvents{
|
HookEvents: webhook_module.HookEvents{
|
||||||
Create: form.Create,
|
Create: form.Create,
|
||||||
Delete: form.Delete,
|
Delete: form.Delete,
|
||||||
Fork: form.Fork,
|
Fork: form.Fork,
|
||||||
Issues: form.Issues,
|
Issues: form.Issues,
|
||||||
IssueAssign: form.IssueAssign,
|
IssueAssign: form.IssueAssign,
|
||||||
IssueLabel: form.IssueLabel,
|
IssueLabel: form.IssueLabel,
|
||||||
IssueMilestone: form.IssueMilestone,
|
IssueMilestone: form.IssueMilestone,
|
||||||
IssueComment: form.IssueComment,
|
IssueComment: form.IssueComment,
|
||||||
Release: form.Release,
|
Release: form.Release,
|
||||||
Push: form.Push,
|
Push: form.Push,
|
||||||
PullRequest: form.PullRequest,
|
PullRequest: form.PullRequest,
|
||||||
PullRequestAssign: form.PullRequestAssign,
|
PullRequestAssign: form.PullRequestAssign,
|
||||||
PullRequestLabel: form.PullRequestLabel,
|
PullRequestLabel: form.PullRequestLabel,
|
||||||
PullRequestMilestone: form.PullRequestMilestone,
|
PullRequestMilestone: form.PullRequestMilestone,
|
||||||
PullRequestComment: form.PullRequestComment,
|
PullRequestComment: form.PullRequestComment,
|
||||||
PullRequestReview: form.PullRequestReview,
|
PullRequestReview: form.PullRequestReview,
|
||||||
PullRequestSync: form.PullRequestSync,
|
PullRequestSync: form.PullRequestSync,
|
||||||
Wiki: form.Wiki,
|
PullRequestReviewRequest: form.PullRequestReviewRequest,
|
||||||
Repository: form.Repository,
|
Wiki: form.Wiki,
|
||||||
Package: form.Package,
|
Repository: form.Repository,
|
||||||
|
Package: form.Package,
|
||||||
},
|
},
|
||||||
BranchFilter: form.BranchFilter,
|
BranchFilter: form.BranchFilter,
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,14 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,30 +228,31 @@ func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) bin
|
||||||
|
|
||||||
// WebhookForm form for changing web hook
|
// WebhookForm form for changing web hook
|
||||||
type WebhookForm struct {
|
type WebhookForm struct {
|
||||||
Events string
|
Events string
|
||||||
Create bool
|
Create bool
|
||||||
Delete bool
|
Delete bool
|
||||||
Fork bool
|
Fork bool
|
||||||
Issues bool
|
Issues bool
|
||||||
IssueAssign bool
|
IssueAssign bool
|
||||||
IssueLabel bool
|
IssueLabel bool
|
||||||
IssueMilestone bool
|
IssueMilestone bool
|
||||||
IssueComment bool
|
IssueComment bool
|
||||||
Release bool
|
Release bool
|
||||||
Push bool
|
Push bool
|
||||||
PullRequest bool
|
PullRequest bool
|
||||||
PullRequestAssign bool
|
PullRequestAssign bool
|
||||||
PullRequestLabel bool
|
PullRequestLabel bool
|
||||||
PullRequestMilestone bool
|
PullRequestMilestone bool
|
||||||
PullRequestComment bool
|
PullRequestComment bool
|
||||||
PullRequestReview bool
|
PullRequestReview bool
|
||||||
PullRequestSync bool
|
PullRequestSync bool
|
||||||
Wiki bool
|
PullRequestReviewRequest bool
|
||||||
Repository bool
|
Wiki bool
|
||||||
Package bool
|
Repository bool
|
||||||
Active bool
|
Package bool
|
||||||
BranchFilter string `binding:"GlobPattern"`
|
Active bool
|
||||||
AuthorizationHeader string
|
BranchFilter string `binding:"GlobPattern"`
|
||||||
|
AuthorizationHeader string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushOnly if the hook will be triggered when push
|
// PushOnly if the hook will be triggered when push
|
||||||
|
|
|
@ -719,6 +719,34 @@ func (m *webhookNotifier) NotifyPullRequestReview(ctx context.Context, pr *issue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
|
@ -43,7 +43,7 @@ func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.
|
||||||
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)
|
||||||
|
|
|
@ -238,6 +238,16 @@
|
||||||
</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>
|
||||||
|
|
||||||
|
|
7
templates/swagger/v1_json.tmpl
generated
7
templates/swagger/v1_json.tmpl
generated
|
@ -19934,6 +19934,13 @@
|
||||||
"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"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue