diff --git a/pkg/services/notifications/email.go b/pkg/services/notifications/email.go index e50646b9662..c60adf605d5 100644 --- a/pkg/services/notifications/email.go +++ b/pkg/services/notifications/email.go @@ -11,17 +11,24 @@ type AttachedFile struct { Content []byte } +// EmbeddedContent struct represents an embedded file. +type EmbeddedContent struct { + Name string + Content []byte +} + // Message is representation of the email message. type Message struct { - To []string - SingleEmail bool - From string - Subject string - Body map[string]string - Info string - ReplyTo []string - EmbeddedFiles []string - AttachedFiles []*AttachedFile + To []string + SingleEmail bool + From string + Subject string + Body map[string]string + Info string + ReplyTo []string + EmbeddedFiles []string + EmbeddedContents []EmbeddedContent + AttachedFiles []*AttachedFile } func setDefaultTemplateData(cfg *setting.Cfg, data map[string]any, u *user.User) { diff --git a/pkg/services/notifications/mailer.go b/pkg/services/notifications/mailer.go index 7382454751f..eceaef3caf4 100644 --- a/pkg/services/notifications/mailer.go +++ b/pkg/services/notifications/mailer.go @@ -112,14 +112,15 @@ func (ns *NotificationService) buildEmailMessage(cmd *SendEmailCommand) (*Messag addr := mail.Address{Name: ns.Cfg.Smtp.FromName, Address: ns.Cfg.Smtp.FromAddress} return &Message{ - To: cmd.To, - SingleEmail: cmd.SingleEmail, - From: addr.String(), - Subject: subject, - Body: body, - EmbeddedFiles: cmd.EmbeddedFiles, - AttachedFiles: buildAttachedFiles(cmd.AttachedFiles), - ReplyTo: cmd.ReplyTo, + To: cmd.To, + SingleEmail: cmd.SingleEmail, + From: addr.String(), + Subject: subject, + Body: body, + EmbeddedFiles: cmd.EmbeddedFiles, + EmbeddedContents: cmd.EmbeddedContents, + AttachedFiles: buildAttachedFiles(cmd.AttachedFiles), + ReplyTo: cmd.ReplyTo, }, nil } diff --git a/pkg/services/notifications/models.go b/pkg/services/notifications/models.go index 364c6e6d262..6f5b1419ea5 100644 --- a/pkg/services/notifications/models.go +++ b/pkg/services/notifications/models.go @@ -18,15 +18,16 @@ type SendEmailAttachFile struct { // SendEmailCommand is the command for sending emails type SendEmailCommand struct { - To []string - SingleEmail bool - Template string - Subject string - Data map[string]any - Info string - ReplyTo []string - EmbeddedFiles []string - AttachedFiles []*SendEmailAttachFile + To []string + SingleEmail bool + Template string + Subject string + Data map[string]any + Info string + ReplyTo []string + EmbeddedFiles []string + EmbeddedContents []EmbeddedContent + AttachedFiles []*SendEmailAttachFile } // SendEmailCommandSync is the command for sending emails synchronously diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index 2be85628090..dc453874d14 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -201,15 +201,16 @@ func __dangerouslyInjectHTML(s string) template.HTML { func (ns *NotificationService) SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmailCommandSync) error { message, err := ns.buildEmailMessage(&SendEmailCommand{ - Data: cmd.Data, - Info: cmd.Info, - Template: cmd.Template, - To: cmd.To, - SingleEmail: cmd.SingleEmail, - EmbeddedFiles: cmd.EmbeddedFiles, - AttachedFiles: cmd.AttachedFiles, - Subject: cmd.Subject, - ReplyTo: cmd.ReplyTo, + Data: cmd.Data, + Info: cmd.Info, + Template: cmd.Template, + To: cmd.To, + SingleEmail: cmd.SingleEmail, + EmbeddedFiles: cmd.EmbeddedFiles, + EmbeddedContents: cmd.EmbeddedContents, + AttachedFiles: cmd.AttachedFiles, + Subject: cmd.Subject, + ReplyTo: cmd.ReplyTo, }) if err != nil { return err diff --git a/pkg/services/notifications/notifications_test.go b/pkg/services/notifications/notifications_test.go index 47c1f56ccb8..b1b3bdfdcaa 100644 --- a/pkg/services/notifications/notifications_test.go +++ b/pkg/services/notifications/notifications_test.go @@ -132,6 +132,31 @@ func TestSendEmailSync(t *testing.T) { require.Equal(t, []byte("text file content"), file.Content) }) + t.Run("When embedding readers to emails", func(t *testing.T) { + ns, mailer := createSut(t, bus) + cmd := &SendEmailCommandSync{ + SendEmailCommand: SendEmailCommand{ + Subject: "subject", + To: []string{"asdf@grafana.com"}, + SingleEmail: true, + Template: "welcome_on_signup", + EmbeddedContents: []EmbeddedContent{ + {Name: "embed.jpg", Content: []byte("image content")}, + }, + }, + } + + err := ns.SendEmailCommandHandlerSync(context.Background(), cmd) + require.NoError(t, err) + + require.NotEmpty(t, mailer.Sent) + sent := mailer.Sent[len(mailer.Sent)-1] + require.Len(t, sent.EmbeddedContents, 1) + f := sent.EmbeddedContents[0] + require.Equal(t, "embed.jpg", f.Name) + require.Equal(t, "image content", string(f.Content)) + }) + t.Run("When SMTP disabled in configuration", func(t *testing.T) { cfg := createSmtpConfig() cfg.Smtp.Enabled = false diff --git a/pkg/services/notifications/smtp.go b/pkg/services/notifications/smtp.go index 9ac0ecff570..c5eea8a958c 100644 --- a/pkg/services/notifications/smtp.go +++ b/pkg/services/notifications/smtp.go @@ -142,6 +142,13 @@ func (sc *SmtpClient) setFiles( m.Embed(file) } + for _, file := range msg.EmbeddedContents { + m.Embed(file.Name, gomail.SetCopyFunc(func(writer io.Writer) error { + _, err := writer.Write(file.Content) + return err + })) + } + for _, file := range msg.AttachedFiles { file := file m.Attach(file.Name, gomail.SetCopyFunc(func(writer io.Writer) error {