Skip to content

Commit 5ddebe4

Browse files
author
tsidei
authored
Merge pull request #89 from mariuskiessling/advanced-error-handling-support
Add support for `AdvanceErrorHandling` when sending a message
2 parents 682e1f9 + 0a7dc9e commit 5ddebe4

File tree

2 files changed

+203
-46
lines changed

2 files changed

+203
-46
lines changed

mailjet_client_test.go

Lines changed: 192 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,36 @@ import (
88
"math/rand"
99
"net/http"
1010
"net/http/httptest"
11+
"reflect"
1112
"testing"
1213
"time"
1314

1415
mailjet "github.com/mailjet/mailjet-apiv3-go/v3"
1516
"github.com/mailjet/mailjet-apiv3-go/v3/resources"
1617
)
1718

18-
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
19+
var (
20+
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
21+
// defaultMessages is the default message passed to the server when an email
22+
// is sent.
23+
defaultMessages = mailjet.MessagesV31{
24+
Info: []mailjet.InfoMessagesV31{
25+
{
26+
From: &mailjet.RecipientV31{
27+
Email: "passenger@mailjet.com",
28+
Name: "passenger",
29+
},
30+
To: &mailjet.RecipientsV31{
31+
mailjet.RecipientV31{
32+
Email: "recipient@company.com",
33+
},
34+
},
35+
Subject: "Send API testing",
36+
TextPart: "SendMail is working!",
37+
},
38+
},
39+
}
40+
)
1941

2042
var (
2143
mux *http.ServeMux
@@ -432,55 +454,185 @@ func TestUnitSendMail(t *testing.T) {
432454
}
433455

434456
func TestSendMailV31(t *testing.T) {
435-
// Here we set the behavior of the http mock
436-
httpClientMocked := mailjet.NewhttpClientMock(true)
437-
httpClientMocked.SendMailV31Func = func(req *http.Request) (*http.Response, error) {
438-
data := mailjet.ResultsV31{
439-
ResultsV31: []mailjet.ResultV31{
440-
{
441-
To: []mailjet.GeneratedMessageV31{
442-
{
443-
Email: "recipient@company.com",
444-
MessageUUID: "ac93d194-1432-4e25-a215-2cb450d4a818",
445-
MessageID: 87,
457+
tests := []struct {
458+
name string
459+
messages mailjet.MessagesV31
460+
mockResponse interface{}
461+
mockStatusCode int
462+
wantResponse *mailjet.ResultsV31
463+
wantErr interface{}
464+
}{
465+
{
466+
name: "sending successful",
467+
messages: defaultMessages,
468+
mockResponse: mailjet.ResultsV31{
469+
ResultsV31: []mailjet.ResultV31{
470+
{
471+
To: []mailjet.GeneratedMessageV31{
472+
{
473+
Email: "recipient@company.com",
474+
MessageUUID: "ac93d194-1432-4e25-a215-2cb450d4a818",
475+
MessageID: 87,
476+
},
446477
},
447478
},
448479
},
449480
},
450-
}
451-
rawBytes, _ := json.Marshal(data)
452-
return &http.Response{
453-
Body: ioutil.NopCloser(bytes.NewBuffer(rawBytes)),
454-
StatusCode: http.StatusOK,
455-
}, nil
456-
}
457-
458-
m := mailjet.NewClient(httpClientMocked, mailjet.NewSMTPClientMock(true))
459-
460-
// We define parameters here to pass to SendMailV31
461-
param := []mailjet.InfoMessagesV31{
462-
mailjet.InfoMessagesV31{
463-
From: &mailjet.RecipientV31{
464-
Email: "passenger@mailjet.com",
465-
Name: "passenger",
481+
mockStatusCode: 200,
482+
wantResponse: &mailjet.ResultsV31{
483+
ResultsV31: []mailjet.ResultV31{
484+
{
485+
To: []mailjet.GeneratedMessageV31{
486+
{
487+
Email: "recipient@company.com",
488+
MessageUUID: "ac93d194-1432-4e25-a215-2cb450d4a818",
489+
MessageID: 87,
490+
},
491+
},
492+
},
493+
},
494+
},
495+
wantErr: nil,
496+
},
497+
{
498+
name: "authorization failed",
499+
messages: defaultMessages,
500+
mockResponse: mailjet.ErrorInfoV31{
501+
Identifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
502+
StatusCode: 401,
503+
Message: "API key authentication/authorization failure. You may be unauthorized to access the API or your API key may be expired. Visit API keys management section to check your keys.",
504+
},
505+
mockStatusCode: 401,
506+
wantResponse: nil,
507+
wantErr: &mailjet.ErrorInfoV31{
508+
Identifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
509+
StatusCode: 401,
510+
Message: "API key authentication/authorization failure. You may be unauthorized to access the API or your API key may be expired. Visit API keys management section to check your keys.",
466511
},
467-
To: &mailjet.RecipientsV31{
468-
mailjet.RecipientV31{
469-
Email: "recipient@company.com",
512+
},
513+
{
514+
name: "simple errors in request",
515+
messages: messagesWithAdvancedErrorHandling(),
516+
mockResponse: mailjet.APIFeedbackErrorsV31{
517+
Messages: []mailjet.APIFeedbackErrorV31{
518+
{
519+
Errors: []mailjet.APIErrorDetailsV31{
520+
{
521+
ErrorCode: "mj-0013",
522+
ErrorIdentifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
523+
StatusCode: 400,
524+
// It is not, but let's suppose it is.
525+
ErrorMessage: "\"recipient@company.com\" is an invalid email address.",
526+
ErrorRelatedTo: []string{"To[0].Email"},
527+
},
528+
},
529+
},
530+
},
531+
},
532+
mockStatusCode: 400,
533+
wantResponse: nil,
534+
wantErr: &mailjet.APIFeedbackErrorsV31{
535+
Messages: []mailjet.APIFeedbackErrorV31{
536+
{
537+
Errors: []mailjet.APIErrorDetailsV31{
538+
{
539+
ErrorCode: "mj-0013",
540+
ErrorIdentifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
541+
StatusCode: 400,
542+
// It is not, but let's suppose it is.
543+
ErrorMessage: "\"recipient@company.com\" is an invalid email address.",
544+
ErrorRelatedTo: []string{"To[0].Email"},
545+
},
546+
},
547+
},
548+
},
549+
},
550+
},
551+
{
552+
name: "advanced error handling failed",
553+
messages: messagesWithAdvancedErrorHandling(),
554+
mockResponse: mailjet.APIFeedbackErrorsV31{
555+
Messages: []mailjet.APIFeedbackErrorV31{
556+
{
557+
Errors: []mailjet.APIErrorDetailsV31{
558+
{
559+
ErrorCode: "send-0008",
560+
ErrorIdentifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
561+
StatusCode: 403,
562+
ErrorMessage: "\"passenger@mailjet.com\" is not an authorized sender email address for your account.",
563+
ErrorRelatedTo: []string{"From"},
564+
},
565+
},
566+
},
567+
},
568+
},
569+
mockStatusCode: 403,
570+
wantResponse: nil,
571+
wantErr: &mailjet.APIFeedbackErrorsV31{
572+
Messages: []mailjet.APIFeedbackErrorV31{
573+
{
574+
Errors: []mailjet.APIErrorDetailsV31{
575+
{
576+
ErrorCode: "send-0008",
577+
ErrorIdentifier: "ac93d194-1432-4e25-a215-2cb450d4a818",
578+
StatusCode: 403,
579+
ErrorMessage: "\"passenger@mailjet.com\" is not an authorized sender email address for your account.",
580+
ErrorRelatedTo: []string{"From"},
581+
},
582+
},
583+
},
470584
},
471585
},
472-
Subject: "Send API testing",
473-
TextPart: "SendMail is working !",
474586
},
475587
}
476588

477-
messages := mailjet.MessagesV31{Info: param}
589+
for _, test := range tests {
590+
t.Run(test.name, func(t *testing.T) {
591+
httpClientMocked := mailjet.NewhttpClientMock(true)
592+
httpClientMocked.SendMailV31Func = func(req *http.Request) (*http.Response, error) {
593+
if req.Header.Get("Content-Type") != "application/json" {
594+
t.Errorf("Wanted request content-type header to be: application/json, got: %s", req.Header.Get("Content-Type"))
595+
}
478596

479-
res, err := m.SendMailV31(&messages)
480-
if err != nil {
481-
t.Fatal("Unexpected error:", err)
482-
}
483-
if res != nil {
484-
t.Logf("Data: %+v\n", res)
597+
user, pass, ok := req.BasicAuth()
598+
if !ok || user != httpClientMocked.APIKeyPublic() || pass != httpClientMocked.APIKeyPrivate() {
599+
t.Errorf("Wanted HTTP basic auth to be: %s/%s, got %s/%s", user, pass,
600+
httpClientMocked.APIKeyPublic(), httpClientMocked.APIKeyPrivate())
601+
}
602+
603+
var msgs mailjet.MessagesV31
604+
err := json.NewDecoder(req.Body).Decode(&msgs)
605+
if err != nil {
606+
t.Fatalf("Could not decode request body and read message information: %v", err)
607+
}
608+
609+
if !reflect.DeepEqual(test.messages, msgs) {
610+
t.Errorf("Wanted request messages: %+v, got: %+v", test.messages, msgs)
611+
}
612+
613+
rawBytes, _ := json.Marshal(test.mockResponse)
614+
return &http.Response{
615+
Body: ioutil.NopCloser(bytes.NewBuffer(rawBytes)),
616+
StatusCode: test.mockStatusCode,
617+
}, nil
618+
}
619+
620+
m := mailjet.NewClient(httpClientMocked, mailjet.NewSMTPClientMock(true))
621+
622+
res, err := m.SendMailV31(&test.messages)
623+
if !reflect.DeepEqual(err, test.wantErr) {
624+
t.Fatalf("Wanted error: %+v, got: %+v", err, test.wantErr)
625+
}
626+
627+
if !reflect.DeepEqual(test.wantResponse, res) {
628+
t.Fatalf("Wanted response: %+v, got %+v", test.wantResponse, res)
629+
}
630+
})
485631
}
486632
}
633+
634+
func messagesWithAdvancedErrorHandling() mailjet.MessagesV31 {
635+
withAdvancedErrorChecking := defaultMessages
636+
withAdvancedErrorChecking.AdvanceErrorHandling = true
637+
return withAdvancedErrorChecking
638+
}

mailjet_resources.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,9 @@ type InfoSMTP struct {
154154

155155
// MessagesV31 definition
156156
type MessagesV31 struct {
157-
Info []InfoMessagesV31 `json:"Messages,omitempty"`
158-
SandBoxMode bool `json:",omitempty"`
157+
Info []InfoMessagesV31 `json:"Messages,omitempty"`
158+
AdvanceErrorHandling bool `json:"AdvanceErrorHandling,omitempty"`
159+
SandBoxMode bool `json:",omitempty"`
159160
}
160161

161162
// InfoMessagesV31 represents the payload input taken by send API v3.1
@@ -231,10 +232,14 @@ func (err *ErrorInfoV31) Error() string {
231232

232233
// APIErrorDetailsV31 contains the information details describing a specific error
233234
type APIErrorDetailsV31 struct {
234-
ErrorClass string
235-
ErrorMessage string
236-
ErrorRelatedTo []string
237-
StatusCode int
235+
// Deprecated: ErrorClass is no longer populated. Use the ErrorCode to
236+
// classify the issue.
237+
ErrorClass string
238+
ErrorCode string
239+
ErrorIdentifier string
240+
ErrorMessage string
241+
ErrorRelatedTo []string
242+
StatusCode int
238243
}
239244

240245
// APIFeedbackErrorV31 struct is composed of an error definition and the payload associated

0 commit comments

Comments
 (0)