diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index ddb043c..848d0cc 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -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/ diff --git a/.gitignore b/.gitignore index 944c2d4..22ccfea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env .envrc zero-notification-service .history/ diff --git a/api/notification-service.yaml b/api/notification-service.yaml index 50ce0d2..bb5d2fb 100644 --- a/api/notification-service.yaml +++ b/api/notification-service.yaml @@ -262,6 +262,8 @@ components: $ref: '#/components/schemas/EmailSender' message: $ref: '#/components/schemas/MailMessage' + headers: + $ref: '#/components/schemas/MailHeaders' SendBulkMailRequest: type: object @@ -285,6 +287,8 @@ components: $ref: '#/components/schemas/EmailSender' message: $ref: '#/components/schemas/MailMessage' + headers: + $ref: '#/components/schemas/MailHeaders' MailMessage: type: object @@ -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: diff --git a/internal/mail/mail.go b/internal/mail/mail.go index 114c871..0fcf211 100644 --- a/internal/mail/mail.go +++ b/internal/mail/mail.go @@ -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) @@ -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)) @@ -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) } diff --git a/internal/mail/mail_test.go b/internal/mail/mail_test.go index 205cb4f..f57ba12 100644 --- a/internal/mail/mail_test.go +++ b/internal/mail/mail_test.go @@ -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 diff --git a/internal/service/api_email_service.go b/internal/service/api_email_service.go index dbb7ef3..69c9f2e 100644 --- a/internal/service/api_email_service.go +++ b/internal/service/api_email_service.go @@ -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) @@ -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