Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add headers support to sendgrid mail #31

Merged
merged 3 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

- name: Generate code
run: |
docker run --rm -v ${GITHUB_WORKSPACE}:/local openapitools/openapi-generator-cli generate -i /local/api/notification-service.yaml -g go-server -o /local/ -p sourceFolder=internal/server -p packageName=server --git-user-id=commitdev --git-repo-id=zero-notification-service
docker run --rm -v ${GITHUB_WORKSPACE}:/local openapitools/openapi-generator:cli-5.3.x generate -i /local/api/notification-service.yaml -g go-server -o /local/ -p sourceFolder=internal/server -p packageName=server --git-user-id=commitdev --git-repo-id=zero-notification-service
sudo chmod -R a+rw ${GITHUB_WORKSPACE}
go get golang.org/x/tools/cmd/goimports
goimports -w ${GITHUB_WORKSPACE}/internal/server/
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env
.envrc
zero-notification-service
.history/
Expand Down
9 changes: 9 additions & 0 deletions api/notification-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ components:
$ref: '#/components/schemas/EmailSender'
message:
$ref: '#/components/schemas/MailMessage'
headers:
$ref: '#/components/schemas/MailHeaders'

SendBulkMailRequest:
type: object
Expand All @@ -285,6 +287,8 @@ components:
$ref: '#/components/schemas/EmailSender'
message:
$ref: '#/components/schemas/MailMessage'
headers:
$ref: '#/components/schemas/MailHeaders'

MailMessage:
type: object
Expand All @@ -305,6 +309,11 @@ components:
description: Schedule these mesages to go out at the time specified by this UNIX timestamp
format: int64

MailHeaders:
type: object
additionalProperties:
type: string

SendSMSRequest:
type: object
required:
Expand Down
12 changes: 9 additions & 3 deletions internal/mail/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ type Client interface {

// SendBulkMail sends a batch of email messages to all the specified recipients
// All the calls to send mail happen in parallel, with their responses returned on the provided channel
func SendBulkMail(toList []server.EmailRecipient, from server.EmailSender, cc []server.EmailRecipient, bcc []server.EmailRecipient, message server.MailMessage, client Client, responseChannel chan BulkSendAttempt) {
func SendBulkMail(toList []server.EmailRecipient, from server.EmailSender, cc []server.EmailRecipient, bcc []server.EmailRecipient, headers map[string]string, message server.MailMessage, client Client, responseChannel chan BulkSendAttempt) {
wg := sync.WaitGroup{}
wg.Add(len(toList))

// Create goroutines for each send
for _, to := range toList {
go func(to server.EmailRecipient) {
response, err := SendIndividualMail([]server.EmailRecipient{to}, from, cc, bcc, message, client)
response, err := SendIndividualMail([]server.EmailRecipient{to}, from, cc, bcc, headers, message, client)
responseChannel <- BulkSendAttempt{to.Address, response, err}
wg.Done()
}(to)
Expand All @@ -42,7 +42,7 @@ func SendBulkMail(toList []server.EmailRecipient, from server.EmailSender, cc []
}

// SendIndividualMail sends an email message
func SendIndividualMail(to []server.EmailRecipient, from server.EmailSender, cc []server.EmailRecipient, bcc []server.EmailRecipient, message server.MailMessage, client Client) (*rest.Response, error) {
func SendIndividualMail(to []server.EmailRecipient, from server.EmailSender, cc []server.EmailRecipient, bcc []server.EmailRecipient, headers map[string]string, message server.MailMessage, client Client) (*rest.Response, error) {
sendMessage := sendgridMail.NewV3Mail()

sendMessage.SetFrom(sendgridMail.NewEmail(from.Name, from.Address))
Expand Down Expand Up @@ -72,6 +72,12 @@ func SendIndividualMail(to []server.EmailRecipient, from server.EmailSender, cc
}
sendMessage.AddPersonalizations(personalization)

if len(headers) > 0 {
for key, value := range headers {
sendMessage.SetHeader(key, value)
}
}

return client.Send(sendMessage)
}

Expand Down
26 changes: 23 additions & 3 deletions internal/mail/mail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,42 @@ type FakeClient struct {

// Mock the Send function
func (cl *FakeClient) Send(email *sendgridMail.SGMailV3) (*rest.Response, error) {
cl.Called()
cl.Called(email)
return nil, nil
}

func TestSendMail(t *testing.T) {
toList := createRandomRecipients(1, 1)
cc := make([]server.EmailRecipient, 0)
bcc := make([]server.EmailRecipient, 0)
from := server.EmailSender{Name: "Test User", Address: "address@domain.com"}
headers := map[string]string{
"X-Test-Header": "Test Header Value",
}
message := server.MailMessage{Subject: "Subject", Body: "Body"}
client := FakeClient{}

headersMatcher := mock.MatchedBy(func(m *sendgridMail.SGMailV3) bool {
return m.Headers["X-Test-Header"] == headers["X-Test-Header"]
})
client.On("Send", headersMatcher).Return(nil, nil)
mail.SendIndividualMail(toList, from, cc, bcc, headers, message, &client)
client.AssertNumberOfCalls(t, "Send", 1)
}

func TestSendBulkMail(t *testing.T) {
toList := createRandomRecipients(2, 5)
cc := make([]server.EmailRecipient, 0)
bcc := make([]server.EmailRecipient, 0)
from := server.EmailSender{Name: "Test User", Address: "address@domain.com"}
headers := make(map[string]string)
message := server.MailMessage{Subject: "Subject", Body: "Body"}
client := FakeClient{}

client.On("Send").Return(nil, nil)
client.On("Send", mock.Anything).Return(nil, nil)

responseChannel := make(chan mail.BulkSendAttempt)
mail.SendBulkMail(toList, from, cc, bcc, message, &client, responseChannel)
mail.SendBulkMail(toList, from, cc, bcc, headers, message, &client, responseChannel)

// Range over the channel until empty
returnedCount := 0
Expand Down
5 changes: 3 additions & 2 deletions internal/service/api_email_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func (s *EmailApiService) SendEmail(ctx context.Context, sendMailRequest server.
}

client := sendgrid.NewSendClient(s.config.SendgridAPIKey)
response, err := mail.SendIndividualMail(sendMailRequest.ToAddresses, sendMailRequest.FromAddress, sendMailRequest.CcAddresses, sendMailRequest.BccAddresses, sendMailRequest.Message, client)

response, err := mail.SendIndividualMail(sendMailRequest.ToAddresses, sendMailRequest.FromAddress, sendMailRequest.CcAddresses, sendMailRequest.BccAddresses, sendMailRequest.Headers, sendMailRequest.Message, client)

if err != nil {
zap.S().Errorf("Error sending mail: %v", response)
Expand Down Expand Up @@ -79,7 +80,7 @@ func (s *EmailApiService) SendBulk(ctx context.Context, sendBulkMailRequest serv

responseChannel := make(chan mail.BulkSendAttempt)

mail.SendBulkMail(sendBulkMailRequest.ToAddresses, sendBulkMailRequest.FromAddress, sendBulkMailRequest.CcAddresses, sendBulkMailRequest.BccAddresses, sendBulkMailRequest.Message, client, responseChannel)
mail.SendBulkMail(sendBulkMailRequest.ToAddresses, sendBulkMailRequest.FromAddress, sendBulkMailRequest.CcAddresses, sendBulkMailRequest.BccAddresses, sendBulkMailRequest.Headers, sendBulkMailRequest.Message, client, responseChannel)

var successful []server.SendBulkMailResponseSuccessful
var failed []server.SendBulkMailResponseFailed
Expand Down