summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author谈笑风生间 <makonike@anyview.fun>2023-05-25 10:06:27 +0800
committerGitHub <noreply@github.com>2023-05-24 22:06:27 -0400
commit309354c70ee994a1e8f261d7bc24e7473e601d02 (patch)
tree89a96f611eef8b37e17dcead9767ff8d9ba976ef
parent93c6a9a652460f89fc719024c435cacbb235d302 (diff)
downloadgitea-309354c70ee994a1e8f261d7bc24e7473e601d02.tar.gz
gitea-309354c70ee994a1e8f261d7bc24e7473e601d02.zip
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.
-rw-r--r--models/issues/pull.go30
-rw-r--r--models/issues/pull_test.go29
-rw-r--r--models/issues/review.go25
-rw-r--r--models/issues/review_test.go17
-rw-r--r--models/user/user.go29
-rw-r--r--models/webhook/webhook.go7
-rw-r--r--models/webhook/webhook_test.go2
-rw-r--r--modules/structs/hook.go21
-rw-r--r--modules/structs/pull.go27
-rw-r--r--modules/webhook/structs.go41
-rw-r--r--modules/webhook/type.go3
-rw-r--r--options/locale/locale_en-US.ini2
-rw-r--r--routers/api/v1/utils/hook.go40
-rw-r--r--routers/web/repo/issue.go2
-rw-r--r--routers/web/repo/webhook.go41
-rw-r--r--services/convert/pull.go8
-rw-r--r--services/forms/repo_form.go49
-rw-r--r--services/webhook/notifier.go28
-rw-r--r--services/webhook/payloader.go2
-rw-r--r--templates/repo/settings/webhook/settings.tmpl10
-rw-r--r--templates/swagger/v1_json.tmpl7
21 files changed, 304 insertions, 116 deletions
diff --git a/models/issues/pull.go b/models/issues/pull.go
index 218a265741..3f37d8d243 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -175,9 +175,10 @@ type PullRequest struct {
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"`
HeadRepo *repo_model.Repository `xorm:"-"`
@@ -302,6 +303,29 @@ func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) {
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.
func (pr *PullRequest) LoadBaseRepo(ctx context.Context) (err error) {
if pr.BaseRepo != nil {
diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go
index 0b06095213..7a183ac312 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
"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) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{
diff --git a/models/issues/review.go b/models/issues/review.go
index ed30bce149..06cf132a48 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -162,6 +162,27 @@ func (r *Review) LoadReviewer(ctx context.Context) (err error) {
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
func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) {
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)
}
-// 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)
sess := db.GetEngine(db.DefaultContext)
diff --git a/models/issues/review_test.go b/models/issues/review_test.go
index 3221496577..0cb621812c 100644
--- a/models/issues/review_test.go
+++ b/models/issues/review_test.go
@@ -132,11 +132,22 @@ func TestGetReviewersByIssueID(t *testing.T) {
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, issues_model.LoadReviewers(db.DefaultContext, allReviews))
if assert.Len(t, allReviews, 3) {
for i, review := range allReviews {
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer)
diff --git a/models/user/user.go b/models/user/user.go
index 57b2117bb9..07d8177b6a 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/auth/openid"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -910,6 +911,15 @@ func GetUserByID(ctx context.Context, id int64) (*User, error) {
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
func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) {
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.
func GetUserByName(ctx context.Context, name string) (*User, error) {
if len(name) == 0 {
diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go
index e3f6b593d9..fc2bbed083 100644
--- a/models/webhook/webhook.go
+++ b/models/webhook/webhook.go
@@ -298,6 +298,12 @@ func (w *Webhook) HasPackageEvent() bool {
(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
func (w *Webhook) EventCheckers() []struct {
Has func() bool
@@ -329,6 +335,7 @@ func (w *Webhook) EventCheckers() []struct {
{w.HasRepositoryEvent, webhook_module.HookEventRepository},
{w.HasReleaseEvent, webhook_module.HookEventRelease},
{w.HasPackageEvent, webhook_module.HookEventPackage},
+ {w.HasPullRequestReviewRequestEvent, webhook_module.HookEventPullRequestReviewRequest},
}
}
diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go
index e05dcaba01..de6568c321 100644
--- a/models/webhook/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -73,7 +73,7 @@ func TestWebhook_EventsArray(t *testing.T) {
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
"pull_request_review_comment", "pull_request_sync", "wiki", "repository", "release",
- "package",
+ "package", "pull_request_review_request",
},
(&Webhook{
HookEvent: &webhook_module.HookEvent{SendEverything: true},
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index df5da6790f..cd91d4bc46 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -342,6 +342,10 @@ const (
HookIssueDemilestoned HookIssueAction = "demilestoned"
// HookIssueReviewed is an issue action for when a pull request is 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.
@@ -381,14 +385,15 @@ type ChangesPayload struct {
// PullRequestPayload represents a payload information of pull request event.
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
diff --git a/modules/structs/pull.go b/modules/structs/pull.go
index f64bb83af9..a4a6f60b05 100644
--- a/modules/structs/pull.go
+++ b/modules/structs/pull.go
@@ -9,19 +9,20 @@ import (
// PullRequest represents a pull request
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"`
DiffURL string `json:"diff_url"`
diff --git a/modules/webhook/structs.go b/modules/webhook/structs.go
index 96012de352..927a91a74c 100644
--- a/modules/webhook/structs.go
+++ b/modules/webhook/structs.go
@@ -5,26 +5,27 @@ package webhook
// HookEvents is a set of web hook events
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.
diff --git a/modules/webhook/type.go b/modules/webhook/type.go
index db4ab17931..7042d391b7 100644
--- a/modules/webhook/type.go
+++ b/modules/webhook/type.go
@@ -26,6 +26,7 @@ const (
HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
HookEventPullRequestSync HookEventType = "pull_request_sync"
+ HookEventPullRequestReviewRequest HookEventType = "pull_request_review_request"
HookEventWiki HookEventType = "wiki"
HookEventRepository HookEventType = "repository"
HookEventRelease HookEventType = "release"
@@ -46,7 +47,7 @@ func (h HookEventType) Event() string {
case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
return "issues"
case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
- HookEventPullRequestSync:
+ HookEventPullRequestSync, HookEventPullRequestReviewRequest:
return "pull_request"
case HookEventIssueComment, HookEventPullRequestComment:
return "issue_comment"
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index d863cc72bf..458c99f7fe 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -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_sync = 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_merge = Pull Request Merge
settings.event_package = Package
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index 44625cc9b8..b62d20a18a 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -179,25 +179,26 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
HookEvent: &webhook_module.HookEvent{
ChooseEvents: true,
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,
},
@@ -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.PullRequestComment = pullHook(form.Events, string(webhook_module.HookEventPullRequestComment))
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))
if err := w.UpdateEvent(); err != nil {
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index d7ea3f12f1..1511716c1e 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -576,7 +576,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
}
ctx.Data["OriginalReviews"] = originalAuthorReviews
- reviews, err := issues_model.GetReviewersByIssueID(issue.ID)
+ reviews, err := issues_model.GetReviewsByIssueID(issue.ID)
if err != nil {
ctx.ServerError("GetReviewersByIssueID", err)
return
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 46b0f49108..5139c0b091 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -160,26 +160,27 @@ func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
SendEverything: form.SendEverything(),
ChooseEvents: form.ChooseEvents(),
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,
}
diff --git a/services/convert/pull.go b/services/convert/pull.go
index 4989e82cd4..598187ca6e 100644
--- a/services/convert/pull.go
+++ b/services/convert/pull.go
@@ -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 {
apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr()
}
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index cacfb64b17..8108a55f7a 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -228,30 +228,31 @@ func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) bin
// WebhookForm form for changing web hook
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
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index 1f7cb8d988..bd25e20805 100644
--- a/services/webhook/notifier.go
+++ b/services/webhook/notifier.go
@@ -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) {
apiPusher := convert.ToUser(ctx, pusher, nil)
apiRepo := convert.ToRepo(ctx, repo, perm.AccessModeNone)
diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go
index 9eff25628b..d53e65fa5e 100644
--- a/services/webhook/payloader.go
+++ b/services/webhook/payloader.go
@@ -43,7 +43,7 @@ func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.
case webhook_module.HookEventPush:
return s.Push(p.(*api.PushPayload))
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))
case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment:
return s.Review(p.(*api.PullRequestPayload), event)
diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl
index 57745c0401..8b03a3c959 100644
--- a/templates/repo/settings/webhook/settings.tmpl
+++ b/templates/repo/settings/webhook/settings.tmpl
@@ -238,6 +238,16 @@
</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>
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 15402ecdfd..03a65184c3 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -19934,6 +19934,13 @@
"type": "string",
"x-go-name": "PatchURL"
},
+ "requested_reviewers": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ },
+ "x-go-name": "RequestedReviewers"
+ },
"state": {
"$ref": "#/definitions/StateType"
},