feat: process notification and send emails (#803)

This commit is contained in:
Akhilesh Pandey 2023-12-06 22:07:29 +00:00 committed by Harness
parent 6915034816
commit 02aac02993
18 changed files with 801 additions and 20 deletions

View File

@ -0,0 +1,96 @@
// 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"
)
type PullReqBranchUpdatedPayload struct {
Base *BasePullReqPayload
Committer *types.PrincipalInfo
NewSHA string
}
func (s *Service) notifyPullReqBranchUpdated(
ctx context.Context,
event *events.Event[*pullreqevents.BranchUpdatedPayload],
) error {
payload, reviewers, err := s.processPullReqBranchUpdatedEvent(ctx, event)
if err != nil {
return fmt.Errorf(
"failed to process %s event for pullReqID %d: %w",
pullreqevents.BranchUpdatedEvent,
event.Payload.PullReqID,
err,
)
}
if len(reviewers) == 0 {
return nil
}
err = s.notificationClient.SendPullReqBranchUpdated(ctx, reviewers, payload)
if err != nil {
return fmt.Errorf(
"failed to send notification for event %s for pullReqID %d: %w",
pullreqevents.BranchUpdatedEvent,
event.Payload.PullReqID,
err,
)
}
return nil
}
func (s *Service) processPullReqBranchUpdatedEvent(
ctx context.Context,
event *events.Event[*pullreqevents.BranchUpdatedPayload],
) (*PullReqBranchUpdatedPayload, []*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)
}
committer, err := s.principalInfoCache.Get(ctx, event.Payload.PrincipalID)
if err != nil {
return nil, nil, fmt.Errorf("failed to get principal info for %d: %w", event.Payload.PrincipalID, err)
}
reviewers, err := s.pullReqReviewersStore.List(ctx, event.Payload.PullReqID)
if err != nil {
return nil, nil,
fmt.Errorf("failed to get reviewers for pull request %d: %w", event.Payload.PullReqID, err)
}
reviewerPrincipals := make([]*types.PrincipalInfo, len(reviewers))
for i, reviewer := range reviewers {
reviewerPrincipals[i], err = s.principalInfoCache.Get(ctx, reviewer.PrincipalID)
if err != nil {
return nil, nil, fmt.Errorf("failed to get principal info for %d: %w", reviewer.PrincipalID, err)
}
}
return &PullReqBranchUpdatedPayload{
Base: base,
NewSHA: event.Payload.NewSHA,
Committer: committer,
}, reviewerPrincipals, nil
}

View File

@ -0,0 +1,33 @@
// 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"
"github.com/harness/gitness/types"
)
// Client is an interface for sending notifications, such as emails, Slack messages etc.
// It is implemented by MailClient and in future we can have other implementations for other channels like Slack etc.
type Client interface {
SendCommentCreated(ctx context.Context, recipients []*types.PrincipalInfo, payload *CommentCreatedPayload) error
SendReviewerAdded(ctx context.Context, recipients []*types.PrincipalInfo, payload *ReviewerAddedPayload) error
SendPullReqBranchUpdated(
ctx context.Context,
recipients []*types.PrincipalInfo,
payload *PullReqBranchUpdatedPayload,
) error
}

View File

@ -0,0 +1,85 @@
// 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"
)
type CommentCreatedPayload struct {
Base *BasePullReqPayload
Commenter *types.PrincipalInfo
Text string
}
func (s *Service) notifyCommentCreated(
ctx context.Context,
event *events.Event[*pullreqevents.CommentCreatedPayload],
) error {
payload, recipients, err := s.processCommentCreatedEvent(ctx, event)
if err != nil {
return fmt.Errorf(
"failed to process %s event for pullReqID %d: %w",
pullreqevents.CommentCreatedEvent,
event.Payload.PullReqID,
err,
)
}
err = s.notificationClient.SendCommentCreated(ctx, recipients, payload)
if err != nil {
return fmt.Errorf(
"failed to send notification for event %s for pullReqID %d: %w",
pullreqevents.CommentCreatedEvent,
event.Payload.PullReqID,
err,
)
}
return nil
}
func (s *Service) processCommentCreatedEvent(
ctx context.Context,
event *events.Event[*pullreqevents.CommentCreatedPayload],
) (*CommentCreatedPayload, []*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)
}
activity, err := s.pullReqActivityStore.Find(ctx, event.Payload.ActivityID)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch activity from pullReqActivityStore: %w", err)
}
commenter, err := s.principalInfoView.Find(ctx, activity.CreatedBy)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch commenter from principalInfoView: %w", err)
}
recipients := []*types.PrincipalInfo{
base.Author,
}
return &CommentCreatedPayload{
Base: base,
Commenter: commenter,
Text: activity.Text,
}, recipients, nil
}

View File

@ -0,0 +1,149 @@
// 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 (
"bytes"
"context"
"fmt"
pullreqevents "github.com/harness/gitness/app/events/pullreq"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/types"
)
const (
TemplateReviewerAdded = "reviewer_added.html"
TemplateCommentCreated = "comment_created.html"
TemplatePullReqBranchUpdated = "pullreq_branch_updated.html"
)
type MailClient struct {
mailer.Mailer
}
func NewMailClient(mailer mailer.Mailer) MailClient {
return MailClient{
Mailer: mailer,
}
}
func (m MailClient) SendCommentCreated(
ctx context.Context,
recipients []*types.PrincipalInfo,
payload *CommentCreatedPayload,
) error {
mailPayload, err := GenerateEmailFromPayload(
TemplateCommentCreated,
recipients,
payload.Base,
payload,
)
if err != nil {
return fmt.Errorf("failed to generate mail requests after processing %s event: %w",
pullreqevents.CommentCreatedEvent, err)
}
return m.Mailer.Send(ctx, *mailPayload)
}
func (m MailClient) SendReviewerAdded(
ctx context.Context,
recipients []*types.PrincipalInfo,
payload *ReviewerAddedPayload,
) error {
reviewerAddedMail, err := GenerateEmailFromPayload(
TemplateReviewerAdded,
recipients,
payload.Base,
payload,
)
if err != nil {
return fmt.Errorf("failed to generate mail requests after processing %s event: %w",
pullreqevents.ReviewerAddedEvent, err)
}
return m.Mailer.Send(ctx, *reviewerAddedMail)
}
func (m MailClient) SendPullReqBranchUpdated(
ctx context.Context,
recipients []*types.PrincipalInfo,
payload *PullReqBranchUpdatedPayload,
) error {
mailPayload, err := GenerateEmailFromPayload(
TemplatePullReqBranchUpdated,
recipients,
payload.Base,
payload,
)
if err != nil {
return fmt.Errorf("failed to generate mail requests after processing %s event: %w",
pullreqevents.BranchUpdatedEvent, err)
}
return m.Mailer.Send(ctx, *mailPayload)
}
func GetSubjectPullRequest(
repoUID string,
prNum int64,
prTitle string,
) string {
return fmt.Sprintf(subjectPullReqEvent, repoUID, prTitle, prNum)
}
func GetHTMLBody(templateName string, data interface{}) ([]byte, error) {
tmpl := htmlTemplates[templateName]
tmplOutput := bytes.Buffer{}
err := tmpl.Execute(&tmplOutput, data)
if err != nil {
return nil, fmt.Errorf("failed to execute template %s", templateName)
}
return tmplOutput.Bytes(), nil
}
func GenerateEmailFromPayload(
templateName string,
recipients []*types.PrincipalInfo,
base *BasePullReqPayload,
payload interface{},
) (*mailer.Payload, error) {
subject := GetSubjectPullRequest(base.Repo.UID, base.PullReq.Number,
base.PullReq.Title)
body, err := GetHTMLBody(templateName, payload)
if err != nil {
return nil, err
}
var mail mailer.Payload
mail.Body = string(body)
mail.Subject = subject
mail.RepoRef = base.Repo.Path
recipientEmails := RetrieveEmailsFromPrincipals(recipients)
mail.ToRecipients = recipientEmails
return &mail, nil
}
func RetrieveEmailsFromPrincipals(principals []*types.PrincipalInfo) []string {
emails := make([]string, len(principals))
for i, principal := range principals {
emails[i] = principal.Email
}
return emails
}

View File

@ -15,12 +15,13 @@
package mailer
import (
"context"
"crypto/tls"
gomail "gopkg.in/mail.v2"
)
type Service struct {
type GoMailClient struct {
dialer *gomail.Dialer
fromMail string
}
@ -32,18 +33,18 @@ func NewMailClient(
fromMail string,
password string,
insecure bool,
) *Service {
) GoMailClient {
d := gomail.NewDialer(host, port, username, password)
d.TLSConfig = &tls.Config{InsecureSkipVerify: insecure} // #nosec G402 (insecure TLS configuration)
return &Service{
return GoMailClient{
dialer: d,
fromMail: fromMail,
}
}
func (c *Service) SendMail(mailRequest *MailRequest) error {
mail := mailRequest.ToGoMail()
func (c GoMailClient) Send(_ context.Context, mailPayload Payload) error {
mail := ToGoMail(mailPayload)
mail.SetHeader("From", c.fromMail)
return c.dialer.DialAndSend(mail)
}

View File

@ -14,21 +14,34 @@
package mailer
import gomail "gopkg.in/mail.v2"
import (
"context"
type MailRequest struct {
CCRecipients []string `json:"cc_recipients"`
ToRecipients []string `json:"to_recipients"`
Subject string `json:"subject"`
Body string `json:"body"`
ContentType string `json:"content_type"`
gomail "gopkg.in/mail.v2"
)
const (
mailContentType = "text/html"
)
type Mailer interface {
Send(ctx context.Context, mailPayload Payload) error
}
func (dto *MailRequest) ToGoMail() *gomail.Message {
type Payload struct {
CCRecipients []string
ToRecipients []string
Subject string
Body string
ContentType string
RepoRef string
}
func ToGoMail(dto Payload) *gomail.Message {
mail := gomail.NewMessage()
mail.SetHeader("To", dto.ToRecipients...)
mail.SetHeader("Cc", dto.CCRecipients...)
mail.SetHeader("Subject", dto.Subject)
mail.SetBody(dto.ContentType, dto.Body)
mail.SetBody(mailContentType, dto.Body)
return mail
}

View File

@ -21,11 +21,11 @@ import (
)
var WireSet = wire.NewSet(
ProvideMailService,
ProvideMailClient,
)
func ProvideMailService(config *types.Config) *Service {
mailSvc := NewMailClient(
func ProvideMailClient(config *types.Config) Mailer {
return NewMailClient(
config.SMTP.Host,
config.SMTP.Port,
config.SMTP.Username,
@ -33,5 +33,4 @@ func ProvideMailService(config *types.Config) *Service {
config.SMTP.Password,
config.SMTP.Insecure, // #nosec G402 (insecure skipVerify configuration)
)
return mailSvc
}

View File

@ -0,0 +1,81 @@
// 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"
)
type ReviewerAddedPayload struct {
Base *BasePullReqPayload
Reviewer *types.PrincipalInfo
}
func (s *Service) notifyReviewerAdded(
ctx context.Context,
event *events.Event[*pullreqevents.ReviewerAddedPayload],
) error {
payload, err := s.processReviewerAddedEvent(ctx, event)
if err != nil {
return fmt.Errorf(
"failed to process %s event for pullReqID %d: %w",
pullreqevents.ReviewerAddedEvent,
event.Payload.PullReqID,
err,
)
}
// 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(
"failed to send notification for event %s for pullReqID %d: %w",
pullreqevents.ReviewerAddedEvent,
event.Payload.PullReqID,
err,
)
}
return nil
}
func (s *Service) processReviewerAddedEvent(
ctx context.Context,
event *events.Event[*pullreqevents.ReviewerAddedPayload],
) (*ReviewerAddedPayload, error) {
base, err := s.getBasePayload(ctx, event.Payload.Base)
if err != nil {
return 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 &ReviewerAddedPayload{
Base: base,
Reviewer: reviewerPrincipal,
}, nil
}

View File

@ -0,0 +1,179 @@
// 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"
"embed"
"fmt"
"html/template"
"io/fs"
"path"
pullreqevents "github.com/harness/gitness/app/events/pullreq"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/events"
"github.com/harness/gitness/stream"
"github.com/harness/gitness/types"
)
const (
eventReaderGroupName = "gitness:notification"
templatesDir = "templates"
subjectPullReqEvent = "[%s] %s (PR #%d)"
)
var (
//go:embed templates/*
files embed.FS
htmlTemplates map[string]*template.Template
)
func init() {
err := LoadTemplates()
if err != nil {
panic(err)
}
}
func LoadTemplates() error {
htmlTemplates = make(map[string]*template.Template)
tmplFiles, err := fs.ReadDir(files, templatesDir)
if err != nil {
return err
}
for _, tmpl := range tmplFiles {
if tmpl.IsDir() {
continue
}
pt, err := template.ParseFS(files, path.Join(templatesDir, tmpl.Name()))
if err != nil {
return err
}
htmlTemplates[tmpl.Name()] = pt
}
return nil
}
type BasePullReqPayload struct {
Repo *types.Repository
PullReq *types.PullReq
Author *types.PrincipalInfo
PullReqURL string
}
type Config struct {
EventReaderName string
Concurrency int
MaxRetries int
}
type Service struct {
config Config
notificationClient Client
prReaderFactory *events.ReaderFactory[*pullreqevents.Reader]
pullReqStore store.PullReqStore
repoStore store.RepoStore
principalInfoView store.PrincipalInfoView
principalInfoCache store.PrincipalInfoCache
pullReqReviewersStore store.PullReqReviewerStore
pullReqActivityStore store.PullReqActivityStore
spacePathStore store.SpacePathStore
urlProvider url.Provider
}
func NewService(
ctx context.Context,
config Config,
notificationClient Client,
prReaderFactory *events.ReaderFactory[*pullreqevents.Reader],
pullReqStore store.PullReqStore,
repoStore store.RepoStore,
principalInfoView store.PrincipalInfoView,
principalInfoCache store.PrincipalInfoCache,
pullReqReviewersStore store.PullReqReviewerStore,
pullReqActivityStore store.PullReqActivityStore,
spacePathStore store.SpacePathStore,
urlProvider url.Provider,
) (*Service, error) {
service := &Service{
config: config,
notificationClient: notificationClient,
prReaderFactory: prReaderFactory,
pullReqStore: pullReqStore,
repoStore: repoStore,
principalInfoView: principalInfoView,
principalInfoCache: principalInfoCache,
pullReqReviewersStore: pullReqReviewersStore,
pullReqActivityStore: pullReqActivityStore,
spacePathStore: spacePathStore,
urlProvider: urlProvider,
}
_, err := service.prReaderFactory.Launch(
ctx,
eventReaderGroupName,
config.EventReaderName,
func(r *pullreqevents.Reader,
) error {
r.Configure(
stream.WithConcurrency(config.Concurrency),
stream.WithHandlerOptions(
stream.WithMaxRetries(config.MaxRetries),
))
_ = r.RegisterReviewerAdded(service.notifyReviewerAdded)
_ = r.RegisterCommentCreated(service.notifyCommentCreated)
_ = r.RegisterBranchUpdated(service.notifyPullReqBranchUpdated)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to launch event reader for %s: %w", eventReaderGroupName, err)
}
return service, nil
}
func (s *Service) getBasePayload(
ctx context.Context,
base pullreqevents.Base,
) (*BasePullReqPayload, error) {
repo, err := s.repoStore.Find(ctx, base.TargetRepoID)
if err != nil {
return nil, fmt.Errorf("failed to fetch repo from repoStore: %w", err)
}
pullReq, err := s.pullReqStore.Find(ctx, base.PullReqID)
if err != nil {
return nil, fmt.Errorf("failed to fetch pullreq from pullReqStore: %w", err)
}
author, err := s.principalInfoCache.Get(ctx, pullReq.CreatedBy)
if err != nil {
return nil,
fmt.Errorf("failed to fetch author %d from principalInfoCache while building base notification: %w",
pullReq.CreatedBy, err)
}
return &BasePullReqPayload{
Repo: repo,
PullReq: pullReq,
Author: author,
PullReqURL: s.urlProvider.GenerateUIPRURL(repo.Path, pullReq.Number),
}, nil
}

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
<b>@{{.Commenter.DisplayName}}</b> commented on pull request <b>#{{.Base.PullReq.Number}}:{{.Base.PullReq.Title}}</b>
</p>
<p>
{{.Text}}
</p>
<p>
<a href="{{.Base.PullReqURL}}">View pull request #{{.Base.PullReq.Number}}</a>
</p>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
<b>@{{.Committer.DisplayName}}</b> pushed new commits to pull request <b>#{{.Base.PullReq.Number}}:{{.Base.PullReq.Title}}</b>
</p>
<p>
Latest commit is {{.NewSHA}}
</p>
<p>
<a href="{{.Base.PullReqURL}}">View pull request #{{.Base.PullReq.Number}}</a>
</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
<b>@{{.Reviewer.DisplayName}}</b> was added as a reviewer for the Pull request: <b>#{{.Base.PullReq.Number}}:{{.Base.PullReq.Title}}</b>
</p>
<p>
<a href="{{.Base.PullReqURL}}">View pull request #{{.Base.PullReq.Number}}</a>
</p>
</body>
</html>

View File

@ -0,0 +1,66 @@
// 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"
pullreqevents "github.com/harness/gitness/app/events/pullreq"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/events"
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvideMailClient,
ProvideNotificationService,
)
func ProvideNotificationService(
ctx context.Context,
notificationClient Client,
pullReqConfig Config,
prReaderFactory *events.ReaderFactory[*pullreqevents.Reader],
pullReqStore store.PullReqStore,
repoStore store.RepoStore,
principalInfoView store.PrincipalInfoView,
principalInfoCache store.PrincipalInfoCache,
pullReqReviewersStore store.PullReqReviewerStore,
pullReqActivityStore store.PullReqActivityStore,
spacePathStore store.SpacePathStore,
urlProvider url.Provider,
) (*Service, error) {
return NewService(
ctx,
pullReqConfig,
notificationClient,
prReaderFactory,
pullReqStore,
repoStore,
principalInfoView,
principalInfoCache,
pullReqReviewersStore,
pullReqActivityStore,
spacePathStore,
urlProvider,
)
}
func ProvideMailClient(mailer mailer.Mailer) Client {
return NewMailClient(mailer)
}

View File

@ -19,6 +19,7 @@ import (
"github.com/harness/gitness/app/services/job"
"github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/metric"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/pullreq"
"github.com/harness/gitness/app/services/trigger"
"github.com/harness/gitness/app/services/webhook"
@ -37,6 +38,7 @@ type Services struct {
JobScheduler *job.Scheduler
MetricCollector *metric.Collector
Cleanup *cleanup.Service
Notification *notification.Service
Keywordsearch *keywordsearch.Service
}
@ -47,6 +49,7 @@ func ProvideServices(
jobScheduler *job.Scheduler,
metricCollector *metric.Collector,
cleanupSvc *cleanup.Service,
notificationSvc *notification.Service,
keywordsearchSvc *keywordsearch.Service,
) Services {
return Services{
@ -56,6 +59,7 @@ func ProvideServices(
JobScheduler: jobScheduler,
MetricCollector: metricCollector,
Cleanup: cleanupSvc,
Notification: notificationSvc,
Keywordsearch: keywordsearchSvc,
}
}

View File

@ -25,6 +25,7 @@ import (
"github.com/harness/gitness/app/services/cleanup"
"github.com/harness/gitness/app/services/codeowners"
"github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/trigger"
"github.com/harness/gitness/app/services/webhook"
"github.com/harness/gitness/blob"
@ -279,6 +280,14 @@ func ProvideWebhookConfig(config *types.Config) webhook.Config {
}
}
func ProvideNotificationConfig(config *types.Config) notification.Config {
return notification.Config{
EventReaderName: config.InstanceID,
Concurrency: config.Notification.Concurrency,
MaxRetries: config.Notification.MaxRetries,
}
}
// ProvideTriggerConfig loads the trigger service config from the main config.
func ProvideTriggerConfig(config *types.Config) trigger.Config {
return trigger.Config{

View File

@ -9,7 +9,6 @@ package main
import (
"context"
checkcontroller "github.com/harness/gitness/app/api/controller/check"
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
@ -56,6 +55,8 @@ import (
"github.com/harness/gitness/app/services/job"
"github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/metric"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/app/services/protection"
pullreqservice "github.com/harness/gitness/app/services/pullreq"
"github.com/harness/gitness/app/services/trigger"
@ -92,6 +93,8 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
cliserver.ProvideDatabaseConfig,
database.WireSet,
cliserver.ProvideBlobStoreConfig,
mailer.WireSet,
notification.WireSet,
blob.WireSet,
dbtx.WireSet,
cache.WireSet,
@ -124,6 +127,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
cliserver.ProvideEventsConfig,
events.WireSet,
cliserver.ProvideWebhookConfig,
cliserver.ProvideNotificationConfig,
webhook.WireSet,
cliserver.ProvideTriggerConfig,
trigger.WireSet,

View File

@ -55,6 +55,8 @@ import (
"github.com/harness/gitness/app/services/job"
"github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/metric"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/pullreq"
trigger2 "github.com/harness/gitness/app/services/trigger"
@ -286,12 +288,19 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
if err != nil {
return nil, err
}
mailerMailer := mailer.ProvideMailClient(config)
notificationClient := notification.ProvideMailClient(mailerMailer)
notificationConfig := server.ProvideNotificationConfig(config)
notificationService, err := notification.ProvideNotificationService(ctx, notificationClient, notificationConfig, eventsReaderFactory, pullReqStore, repoStore, principalInfoView, principalInfoCache, pullReqReviewerStore, pullReqActivityStore, spacePathStore, provider)
if err != nil {
return nil, err
}
keywordsearchConfig := server.ProvideKeywordSearchConfig(config)
keywordsearchService, err := keywordsearch.ProvideService(ctx, keywordsearchConfig, readerFactory, repoStore, indexer)
if err != nil {
return nil, err
}
servicesServices := services.ProvideServices(webhookService, pullreqService, triggerService, jobScheduler, collector, cleanupService, keywordsearchService)
servicesServices := services.ProvideServices(webhookService, pullreqService, triggerService, jobScheduler, collector, cleanupService, notificationService, keywordsearchService)
serverSystem := server.NewSystem(bootstrapBootstrap, serverServer, poller, pluginManager, servicesServices)
return serverSystem, nil
}

View File

@ -324,6 +324,11 @@ type Config struct {
Insecure bool `envconfig:"GITNESS_SMTP_INSECURE"`
}
Notification struct {
MaxRetries int `envconfig:"GITNESS_NOTIFICATION_MAX_RETRIES" default:"3"`
Concurrency int `envconfig:"GITNESS_NOTIFICATION_CONCURRENCY" default:"4"`
}
KeywordSearch struct {
Concurrency int `envconfig:"GITNESS_KEYWORD_SEARCH_CONCURRENCY" default:"4"`
MaxRetries int `envconfig:"GITNESS_KEYWORD_SEARCH_MAX_RETRIES" default:"3"`