Skip to content

Commit df71fe3

Browse files
committed
refactor: refactoring notification service to use new service registry hooks
1 parent 28f7b6d commit df71fe3

File tree

7 files changed

+93
-111
lines changed

7 files changed

+93
-111
lines changed

pkg/api/org_invite.go

+2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ func AddOrgInvite(c *m.ReqContext, inviteDto dtos.AddInviteForm) Response {
6060
}
6161

6262
// send invite email
63+
c.Logger.Error("sending?")
6364
if inviteDto.SendEmail && util.IsEmail(inviteDto.LoginOrEmail) {
65+
c.Logger.Error("yes sending?")
6466
emailCmd := m.SendEmailCommand{
6567
To: []string{inviteDto.LoginOrEmail},
6668
Template: "new_user_invite.html",

pkg/cmd/grafana-server/server.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ import (
2626
"github.com/grafana/grafana/pkg/login"
2727
"github.com/grafana/grafana/pkg/metrics"
2828
"github.com/grafana/grafana/pkg/plugins"
29-
"github.com/grafana/grafana/pkg/services/notifications"
3029
"github.com/grafana/grafana/pkg/services/sqlstore"
3130
"github.com/grafana/grafana/pkg/setting"
3231

3332
"github.com/grafana/grafana/pkg/social"
3433
"github.com/grafana/grafana/pkg/tracing"
3534

35+
// self registering services
3636
_ "github.com/grafana/grafana/pkg/extensions"
3737
_ "github.com/grafana/grafana/pkg/services/alerting"
3838
_ "github.com/grafana/grafana/pkg/services/cleanup"
39+
_ "github.com/grafana/grafana/pkg/services/notifications"
3940
_ "github.com/grafana/grafana/pkg/services/search"
4041
)
4142

@@ -56,9 +57,9 @@ type GrafanaServerImpl struct {
5657
shutdownFn context.CancelFunc
5758
childRoutines *errgroup.Group
5859
log log.Logger
59-
RouteRegister api.RouteRegister `inject:""`
6060

61-
HttpServer *api.HTTPServer `inject:""`
61+
RouteRegister api.RouteRegister `inject:""`
62+
HttpServer *api.HTTPServer `inject:""`
6263
}
6364

6465
func (g *GrafanaServerImpl) Start() error {
@@ -89,10 +90,6 @@ func (g *GrafanaServerImpl) Start() error {
8990
}
9091
defer tracingCloser.Close()
9192

92-
if err = notifications.Init(); err != nil {
93-
return fmt.Errorf("Notification service failed to initialize. error: %v", err)
94-
}
95-
9693
serviceGraph := inject.Graph{}
9794
serviceGraph.Provide(&inject.Object{Value: bus.GetBus()})
9895
serviceGraph.Provide(&inject.Object{Value: dashboards.NewProvisioningService()})

pkg/services/notifications/mailer.go

-32
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,12 @@ import (
1111
"html/template"
1212
"net"
1313
"strconv"
14-
"strings"
1514

16-
"github.com/grafana/grafana/pkg/log"
1715
m "github.com/grafana/grafana/pkg/models"
1816
"github.com/grafana/grafana/pkg/setting"
1917
gomail "gopkg.in/mail.v2"
2018
)
2119

22-
var mailQueue chan *Message
23-
24-
func initMailQueue() {
25-
mailQueue = make(chan *Message, 10)
26-
go processMailQueue()
27-
}
28-
29-
func processMailQueue() {
30-
for {
31-
select {
32-
case msg := <-mailQueue:
33-
num, err := send(msg)
34-
tos := strings.Join(msg.To, "; ")
35-
info := ""
36-
if err != nil {
37-
if len(msg.Info) > 0 {
38-
info = ", info: " + msg.Info
39-
}
40-
log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
41-
} else {
42-
log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
43-
}
44-
}
45-
}
46-
}
47-
48-
var addToMailQueue = func(msg *Message) {
49-
mailQueue <- msg
50-
}
51-
5220
func send(msg *Message) (int, error) {
5321
dialer, err := createDialer()
5422
if err != nil {

pkg/services/notifications/notifications.go

+66-25
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"html/template"
88
"net/url"
99
"path/filepath"
10+
"strings"
1011

1112
"github.com/grafana/grafana/pkg/bus"
1213
"github.com/grafana/grafana/pkg/events"
1314
"github.com/grafana/grafana/pkg/log"
1415
m "github.com/grafana/grafana/pkg/models"
16+
"github.com/grafana/grafana/pkg/registry"
1517
"github.com/grafana/grafana/pkg/setting"
1618
"github.com/grafana/grafana/pkg/util"
1719
)
@@ -21,20 +23,31 @@ var tmplResetPassword = "reset_password.html"
2123
var tmplSignUpStarted = "signup_started.html"
2224
var tmplWelcomeOnSignUp = "welcome_on_signup.html"
2325

24-
func Init() error {
25-
initMailQueue()
26-
initWebhookQueue()
26+
func init() {
27+
registry.RegisterService(&NotificationService{})
28+
}
29+
30+
type NotificationService struct {
31+
Bus bus.Bus `inject:""`
32+
mailQueue chan *Message
33+
webhookQueue chan *Webhook
34+
log log.Logger
35+
}
2736

28-
bus.AddHandler("email", sendResetPasswordEmail)
29-
bus.AddHandler("email", validateResetPasswordCode)
30-
bus.AddHandler("email", sendEmailCommandHandler)
37+
func (ns *NotificationService) Init() error {
38+
ns.log = log.New("notifications")
39+
ns.mailQueue = make(chan *Message, 10)
40+
ns.webhookQueue = make(chan *Webhook, 10)
3141

32-
bus.AddCtxHandler("email", sendEmailCommandHandlerSync)
42+
ns.Bus.AddHandler(ns.sendResetPasswordEmail)
43+
ns.Bus.AddHandler(ns.validateResetPasswordCode)
44+
ns.Bus.AddHandler(ns.sendEmailCommandHandler)
3345

34-
bus.AddCtxHandler("webhook", SendWebhookSync)
46+
ns.Bus.AddCtxHandler(ns.sendEmailCommandHandlerSync)
47+
ns.Bus.AddCtxHandler(ns.SendWebhookSync)
3548

36-
bus.AddEventListener(signUpStartedHandler)
37-
bus.AddEventListener(signUpCompletedHandler)
49+
ns.Bus.AddEventListener(ns.signUpStartedHandler)
50+
ns.Bus.AddEventListener(ns.signUpCompletedHandler)
3851

3952
mailTemplates = template.New("name")
4053
mailTemplates.Funcs(template.FuncMap{
@@ -58,8 +71,37 @@ func Init() error {
5871
return nil
5972
}
6073

61-
func SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error {
62-
return sendWebRequestSync(ctx, &Webhook{
74+
func (ns *NotificationService) Run(ctx context.Context) error {
75+
for {
76+
select {
77+
case webhook := <-ns.webhookQueue:
78+
err := ns.sendWebRequestSync(context.Background(), webhook)
79+
80+
if err != nil {
81+
ns.log.Error("Failed to send webrequest ", "error", err)
82+
}
83+
case msg := <-ns.mailQueue:
84+
num, err := send(msg)
85+
tos := strings.Join(msg.To, "; ")
86+
info := ""
87+
if err != nil {
88+
if len(msg.Info) > 0 {
89+
info = ", info: " + msg.Info
90+
}
91+
ns.log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
92+
} else {
93+
ns.log.Debug(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
94+
}
95+
case <-ctx.Done():
96+
return ctx.Err()
97+
}
98+
}
99+
100+
return nil
101+
}
102+
103+
func (ns *NotificationService) SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error {
104+
return ns.sendWebRequestSync(ctx, &Webhook{
63105
Url: cmd.Url,
64106
User: cmd.User,
65107
Password: cmd.Password,
@@ -74,7 +116,7 @@ func subjectTemplateFunc(obj map[string]interface{}, value string) string {
74116
return ""
75117
}
76118

77-
func sendEmailCommandHandlerSync(ctx context.Context, cmd *m.SendEmailCommandSync) error {
119+
func (ns *NotificationService) sendEmailCommandHandlerSync(ctx context.Context, cmd *m.SendEmailCommandSync) error {
78120
message, err := buildEmailMessage(&m.SendEmailCommand{
79121
Data: cmd.Data,
80122
Info: cmd.Info,
@@ -89,24 +131,22 @@ func sendEmailCommandHandlerSync(ctx context.Context, cmd *m.SendEmailCommandSyn
89131
}
90132

91133
_, err = send(message)
92-
93134
return err
94135
}
95136

96-
func sendEmailCommandHandler(cmd *m.SendEmailCommand) error {
137+
func (ns *NotificationService) sendEmailCommandHandler(cmd *m.SendEmailCommand) error {
97138
message, err := buildEmailMessage(cmd)
98139

99140
if err != nil {
100141
return err
101142
}
102143

103-
addToMailQueue(message)
104-
144+
ns.mailQueue <- message
105145
return nil
106146
}
107147

108-
func sendResetPasswordEmail(cmd *m.SendResetPasswordEmailCommand) error {
109-
return sendEmailCommandHandler(&m.SendEmailCommand{
148+
func (ns *NotificationService) sendResetPasswordEmail(cmd *m.SendResetPasswordEmailCommand) error {
149+
return ns.sendEmailCommandHandler(&m.SendEmailCommand{
110150
To: []string{cmd.User.Email},
111151
Template: tmplResetPassword,
112152
Data: map[string]interface{}{
@@ -116,7 +156,7 @@ func sendResetPasswordEmail(cmd *m.SendResetPasswordEmailCommand) error {
116156
})
117157
}
118158

119-
func validateResetPasswordCode(query *m.ValidateResetPasswordCodeQuery) error {
159+
func (ns *NotificationService) validateResetPasswordCode(query *m.ValidateResetPasswordCodeQuery) error {
120160
login := getLoginForEmailCode(query.Code)
121161
if login == "" {
122162
return m.ErrInvalidEmailCode
@@ -135,18 +175,18 @@ func validateResetPasswordCode(query *m.ValidateResetPasswordCodeQuery) error {
135175
return nil
136176
}
137177

138-
func signUpStartedHandler(evt *events.SignUpStarted) error {
178+
func (ns *NotificationService) signUpStartedHandler(evt *events.SignUpStarted) error {
139179
if !setting.VerifyEmailEnabled {
140180
return nil
141181
}
142182

143-
log.Info("User signup started: %s", evt.Email)
183+
ns.log.Info("User signup started", "email", evt.Email)
144184

145185
if evt.Email == "" {
146186
return nil
147187
}
148188

149-
err := sendEmailCommandHandler(&m.SendEmailCommand{
189+
err := ns.sendEmailCommandHandler(&m.SendEmailCommand{
150190
To: []string{evt.Email},
151191
Template: tmplSignUpStarted,
152192
Data: map[string]interface{}{
@@ -155,6 +195,7 @@ func signUpStartedHandler(evt *events.SignUpStarted) error {
155195
"SignUpUrl": setting.ToAbsUrl(fmt.Sprintf("signup/?email=%s&code=%s", url.QueryEscape(evt.Email), url.QueryEscape(evt.Code))),
156196
},
157197
})
198+
158199
if err != nil {
159200
return err
160201
}
@@ -163,12 +204,12 @@ func signUpStartedHandler(evt *events.SignUpStarted) error {
163204
return bus.Dispatch(&emailSentCmd)
164205
}
165206

166-
func signUpCompletedHandler(evt *events.SignUpCompleted) error {
207+
func (ns *NotificationService) signUpCompletedHandler(evt *events.SignUpCompleted) error {
167208
if evt.Email == "" || !setting.Smtp.SendWelcomeEmailOnSignUp {
168209
return nil
169210
}
170211

171-
return sendEmailCommandHandler(&m.SendEmailCommand{
212+
return ns.sendEmailCommandHandler(&m.SendEmailCommand{
172213
To: []string{evt.Email},
173214
Template: tmplWelcomeOnSignUp,
174215
Data: map[string]interface{}{

pkg/services/notifications/notifications_test.go

+8-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package notifications
33
import (
44
"testing"
55

6+
"github.com/grafana/grafana/pkg/bus"
67
m "github.com/grafana/grafana/pkg/models"
78
"github.com/grafana/grafana/pkg/setting"
89
. "github.com/smartystreets/goconvey/convey"
@@ -17,25 +18,23 @@ type testTriggeredAlert struct {
1718
func TestNotifications(t *testing.T) {
1819

1920
Convey("Given the notifications service", t, func() {
20-
//bus.ClearBusHandlers()
21-
2221
setting.StaticRootPath = "../../../public/"
2322
setting.Smtp.Enabled = true
2423
setting.Smtp.TemplatesPattern = "emails/*.html"
2524
setting.Smtp.FromAddress = "[email protected]"
2625
setting.Smtp.FromName = "Grafana Admin"
2726

28-
err := Init()
29-
So(err, ShouldBeNil)
27+
ns := &NotificationService{}
28+
ns.Bus = bus.New()
3029

31-
var sentMsg *Message
32-
addToMailQueue = func(msg *Message) {
33-
sentMsg = msg
34-
}
30+
err := ns.Init()
31+
So(err, ShouldBeNil)
3532

3633
Convey("When sending reset email password", func() {
37-
err := sendResetPasswordEmail(&m.SendResetPasswordEmailCommand{User: &m.User{Email: "[email protected]"}})
34+
err := ns.sendResetPasswordEmail(&m.SendResetPasswordEmailCommand{User: &m.User{Email: "[email protected]"}})
3835
So(err, ShouldBeNil)
36+
37+
sentMsg := <-ns.mailQueue
3938
So(sentMsg.Body, ShouldContainSubstring, "body")
4039
So(sentMsg.Subject, ShouldEqual, "Reset your Grafana password - [email protected]")
4140
So(sentMsg.Body, ShouldNotContainSubstring, "Subject")

pkg/services/notifications/send_email_integration_test.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,18 @@ import (
1212

1313
func TestEmailIntegrationTest(t *testing.T) {
1414
SkipConvey("Given the notifications service", t, func() {
15-
bus.ClearBusHandlers()
16-
1715
setting.StaticRootPath = "../../../public/"
1816
setting.Smtp.Enabled = true
1917
setting.Smtp.TemplatesPattern = "emails/*.html"
2018
setting.Smtp.FromAddress = "[email protected]"
2119
setting.Smtp.FromName = "Grafana Admin"
2220
setting.BuildVersion = "4.0.0"
2321

24-
err := Init()
25-
So(err, ShouldBeNil)
22+
ns := &NotificationService{}
23+
ns.Bus = bus.New()
2624

27-
addToMailQueue = func(msg *Message) {
28-
So(msg.From, ShouldEqual, "Grafana Admin <[email protected]>")
29-
So(msg.To[0], ShouldEqual, "[email protected]")
30-
ioutil.WriteFile("../../../tmp/test_email.html", []byte(msg.Body), 0777)
31-
}
25+
err := ns.Init()
26+
So(err, ShouldBeNil)
3227

3328
Convey("When sending reset email password", func() {
3429
cmd := &m.SendEmailCommand{
@@ -59,8 +54,13 @@ func TestEmailIntegrationTest(t *testing.T) {
5954
Template: "alert_notification.html",
6055
}
6156

62-
err := sendEmailCommandHandler(cmd)
57+
err := ns.sendEmailCommandHandler(cmd)
6358
So(err, ShouldBeNil)
59+
60+
sentMsg := <-ns.mailQueue
61+
So(sentMsg.From, ShouldEqual, "Grafana Admin <[email protected]>")
62+
So(sentMsg.To[0], ShouldEqual, "[email protected]")
63+
ioutil.WriteFile("../../../tmp/test_email.html", []byte(sentMsg.Body), 0777)
6464
})
6565
})
6666
}

0 commit comments

Comments
 (0)