mirror of
https://github.com/harness/drone.git
synced 2025-05-19 02:20:03 +08:00
feat: Add notifications support and templates for PR reviewer decision (#881)
This commit is contained in:
parent
9a4e4fa74d
commit
880f62a857
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
events "github.com/harness/gitness/app/events/pullreq"
|
||||
"github.com/harness/gitness/git"
|
||||
"github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/types"
|
||||
@ -107,6 +108,11 @@ func (c *Controller) ReviewSubmit(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.eventReporter.ReviewSubmitted(ctx, &events.ReviewSubmittedPayload{
|
||||
Base: eventBase(pr, &session.Principal),
|
||||
Decision: review.Decision,
|
||||
ReviewerID: review.CreatedBy,
|
||||
})
|
||||
|
||||
_, err = c.updateReviewer(ctx, session, pr, review, commitSHA)
|
||||
return err
|
||||
|
56
app/events/pullreq/events_review_submitted.go
Normal file
56
app/events/pullreq/events_review_submitted.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/events"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const ReviewSubmittedEvent events.EventType = "review-submitted"
|
||||
|
||||
type ReviewSubmittedPayload struct {
|
||||
Base
|
||||
ReviewerID int64
|
||||
Decision enum.PullReqReviewDecision
|
||||
}
|
||||
|
||||
func (r *Reporter) ReviewSubmitted(
|
||||
ctx context.Context,
|
||||
payload *ReviewSubmittedPayload,
|
||||
) {
|
||||
if payload == nil {
|
||||
return
|
||||
}
|
||||
|
||||
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, ReviewSubmittedEvent, payload)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Err(err).Msgf("failed to send pull request review submitted event")
|
||||
return
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Debug().Msgf("reported pull request review submitted event with id '%s'", eventID)
|
||||
}
|
||||
|
||||
func (r *Reader) RegisterReviewSubmitted(
|
||||
fn events.HandlerFunc[*ReviewSubmittedPayload],
|
||||
opts ...events.HandlerOption,
|
||||
) error {
|
||||
return events.ReaderRegisterEvent(r.innerReader, ReviewSubmittedEvent, fn, opts...)
|
||||
}
|
@ -30,4 +30,5 @@ type Client interface {
|
||||
recipients []*types.PrincipalInfo,
|
||||
payload *PullReqBranchUpdatedPayload,
|
||||
) error
|
||||
SendReviewSubmitted(ctx context.Context, recipients []*types.PrincipalInfo, payload *ReviewSubmittedPayload) error
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ const (
|
||||
TemplateReviewerAdded = "reviewer_added.html"
|
||||
TemplateCommentCreated = "comment_created.html"
|
||||
TemplatePullReqBranchUpdated = "pullreq_branch_updated.html"
|
||||
TemplateNameReviewSubmitted = "review_submitted.html"
|
||||
)
|
||||
|
||||
type MailClient struct {
|
||||
@ -45,7 +46,7 @@ func (m MailClient) SendCommentCreated(
|
||||
recipients []*types.PrincipalInfo,
|
||||
payload *CommentCreatedPayload,
|
||||
) error {
|
||||
mailPayload, err := GenerateEmailFromPayload(
|
||||
email, err := GenerateEmailFromPayload(
|
||||
TemplateCommentCreated,
|
||||
recipients,
|
||||
payload.Base,
|
||||
@ -56,7 +57,7 @@ func (m MailClient) SendCommentCreated(
|
||||
pullreqevents.CommentCreatedEvent, err)
|
||||
}
|
||||
|
||||
return m.Mailer.Send(ctx, *mailPayload)
|
||||
return m.Mailer.Send(ctx, *email)
|
||||
}
|
||||
|
||||
func (m MailClient) SendReviewerAdded(
|
||||
@ -64,7 +65,7 @@ func (m MailClient) SendReviewerAdded(
|
||||
recipients []*types.PrincipalInfo,
|
||||
payload *ReviewerAddedPayload,
|
||||
) error {
|
||||
reviewerAddedMail, err := GenerateEmailFromPayload(
|
||||
email, err := GenerateEmailFromPayload(
|
||||
TemplateReviewerAdded,
|
||||
recipients,
|
||||
payload.Base,
|
||||
@ -75,7 +76,7 @@ func (m MailClient) SendReviewerAdded(
|
||||
pullreqevents.ReviewerAddedEvent, err)
|
||||
}
|
||||
|
||||
return m.Mailer.Send(ctx, *reviewerAddedMail)
|
||||
return m.Mailer.Send(ctx, *email)
|
||||
}
|
||||
|
||||
func (m MailClient) SendPullReqBranchUpdated(
|
||||
@ -83,7 +84,7 @@ func (m MailClient) SendPullReqBranchUpdated(
|
||||
recipients []*types.PrincipalInfo,
|
||||
payload *PullReqBranchUpdatedPayload,
|
||||
) error {
|
||||
mailPayload, err := GenerateEmailFromPayload(
|
||||
email, err := GenerateEmailFromPayload(
|
||||
TemplatePullReqBranchUpdated,
|
||||
recipients,
|
||||
payload.Base,
|
||||
@ -94,7 +95,23 @@ func (m MailClient) SendPullReqBranchUpdated(
|
||||
pullreqevents.BranchUpdatedEvent, err)
|
||||
}
|
||||
|
||||
return m.Mailer.Send(ctx, *mailPayload)
|
||||
return m.Mailer.Send(ctx, *email)
|
||||
}
|
||||
|
||||
func (m MailClient) SendReviewSubmitted(
|
||||
ctx context.Context,
|
||||
recipients []*types.PrincipalInfo,
|
||||
payload *ReviewSubmittedPayload,
|
||||
) error {
|
||||
email, err := GenerateEmailFromPayload(TemplateNameReviewSubmitted, recipients, payload.Base, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to generate mail requests after processing %s event: %w",
|
||||
pullreqevents.ReviewSubmittedEvent,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return m.Mailer.Send(ctx, *email)
|
||||
}
|
||||
|
||||
func GetSubjectPullRequest(
|
||||
@ -130,14 +147,14 @@ func GenerateEmailFromPayload(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mail mailer.Payload
|
||||
mail.Body = string(body)
|
||||
mail.Subject = subject
|
||||
mail.RepoRef = base.Repo.Path
|
||||
var email mailer.Payload
|
||||
email.Body = string(body)
|
||||
email.Subject = subject
|
||||
email.RepoRef = base.Repo.Path
|
||||
|
||||
recipientEmails := RetrieveEmailsFromPrincipals(recipients)
|
||||
mail.ToRecipients = recipientEmails
|
||||
return &mail, nil
|
||||
email.ToRecipients = recipientEmails
|
||||
return &email, nil
|
||||
}
|
||||
|
||||
func RetrieveEmailsFromPrincipals(principals []*types.PrincipalInfo) []string {
|
||||
|
100
app/services/notification/review_submitted.go
Normal file
100
app/services/notification/review_submitted.go
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
pullreqevents "github.com/harness/gitness/app/events/pullreq"
|
||||
"github.com/harness/gitness/events"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
type ReviewSubmittedPayload struct {
|
||||
Base *BasePullReqPayload
|
||||
Author *types.PrincipalInfo
|
||||
Reviewer *types.PrincipalInfo
|
||||
Decision enum.PullReqReviewDecision
|
||||
}
|
||||
|
||||
func (s *Service) notifyReviewSubmitted(
|
||||
ctx context.Context,
|
||||
event *events.Event[*pullreqevents.ReviewSubmittedPayload],
|
||||
) error {
|
||||
notificationPayload, recipients, err := s.processReviewSubmittedEvent(ctx, event)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to process %s event for pullReqID %d: %w",
|
||||
pullreqevents.ReviewSubmittedEvent,
|
||||
event.Payload.PullReqID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
err = s.notificationClient.SendReviewSubmitted(
|
||||
ctx,
|
||||
recipients,
|
||||
notificationPayload,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to send notification for event %s for pullReqID %d: %w",
|
||||
pullreqevents.ReviewSubmittedEvent,
|
||||
event.Payload.PullReqID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) processReviewSubmittedEvent(
|
||||
ctx context.Context,
|
||||
event *events.Event[*pullreqevents.ReviewSubmittedPayload],
|
||||
) (*ReviewSubmittedPayload, []*types.PrincipalInfo, error) {
|
||||
base, err := s.getBasePayload(ctx, event.Payload.Base)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get base payload: %w", err)
|
||||
}
|
||||
|
||||
authorPrincipal, err := s.principalInfoCache.Get(ctx, base.PullReq.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"failed to get author from principalInfoCache on %s event for pullReqID %d: %w",
|
||||
pullreqevents.ReviewSubmittedEvent,
|
||||
event.Payload.PullReqID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
reviewerPrincipal, err := s.principalInfoCache.Get(ctx, event.Payload.ReviewerID)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"failed to get reviewer from principalInfoCache on event %s for pullReqID %d: %w",
|
||||
pullreqevents.ReviewSubmittedEvent,
|
||||
event.Payload.PullReqID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return &ReviewSubmittedPayload{
|
||||
Base: base,
|
||||
Author: authorPrincipal,
|
||||
Decision: event.Payload.Decision,
|
||||
Reviewer: reviewerPrincipal,
|
||||
}, []*types.PrincipalInfo{authorPrincipal}, nil
|
||||
}
|
@ -32,7 +32,7 @@ func (s *Service) notifyReviewerAdded(
|
||||
ctx context.Context,
|
||||
event *events.Event[*pullreqevents.ReviewerAddedPayload],
|
||||
) error {
|
||||
payload, err := s.processReviewerAddedEvent(ctx, event)
|
||||
payload, recipients, err := s.processReviewerAddedEvent(ctx, event)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to process %s event for pullReqID %d: %w",
|
||||
@ -42,11 +42,6 @@ func (s *Service) notifyReviewerAdded(
|
||||
)
|
||||
}
|
||||
|
||||
// Send notification to author and reviewer
|
||||
recipients := []*types.PrincipalInfo{
|
||||
payload.Base.Author,
|
||||
payload.Reviewer,
|
||||
}
|
||||
err = s.notificationClient.SendReviewerAdded(ctx, recipients, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
@ -63,19 +58,24 @@ func (s *Service) notifyReviewerAdded(
|
||||
func (s *Service) processReviewerAddedEvent(
|
||||
ctx context.Context,
|
||||
event *events.Event[*pullreqevents.ReviewerAddedPayload],
|
||||
) (*ReviewerAddedPayload, error) {
|
||||
) (*ReviewerAddedPayload, []*types.PrincipalInfo, error) {
|
||||
base, err := s.getBasePayload(ctx, event.Payload.Base)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get base payload: %w", err)
|
||||
return nil, nil, fmt.Errorf("failed to get base payload: %w", err)
|
||||
}
|
||||
|
||||
reviewerPrincipal, err := s.principalInfoCache.Get(ctx, event.Payload.ReviewerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get reviewer from principalInfoCache: %w", err)
|
||||
return nil, nil, fmt.Errorf("failed to get reviewer from principalInfoCache: %w", err)
|
||||
}
|
||||
|
||||
recipients := []*types.PrincipalInfo{
|
||||
base.Author,
|
||||
reviewerPrincipal,
|
||||
}
|
||||
|
||||
return &ReviewerAddedPayload{
|
||||
Base: base,
|
||||
Reviewer: reviewerPrincipal,
|
||||
}, nil
|
||||
}, recipients, nil
|
||||
}
|
||||
|
@ -141,6 +141,7 @@ func NewService(
|
||||
_ = r.RegisterReviewerAdded(service.notifyReviewerAdded)
|
||||
_ = r.RegisterCommentCreated(service.notifyCommentCreated)
|
||||
_ = r.RegisterBranchUpdated(service.notifyPullReqBranchUpdated)
|
||||
_ = r.RegisterReviewSubmitted(service.notifyReviewSubmitted)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
|
20
app/services/notification/templates/review_submitted.html
Normal file
20
app/services/notification/templates/review_submitted.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>@{{.Reviewer.DisplayName}}</b>
|
||||
{{if eq .Decision "approved"}}
|
||||
approved
|
||||
{{else if eq .Decision "changereq"}}
|
||||
requested changes to
|
||||
{{end}}
|
||||
pull request #{{.Base.PullReq.Number}} {{.Base.PullReq.Title}}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{.Base.PullReqURL}}">View pull request #{{.Base.PullReq.Number}}</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user