From 786a682ffbf632c6a4d175bc366fb8cac31392b8 Mon Sep 17 00:00:00 2001 From: Alaric Whitney Date: Wed, 17 Aug 2022 19:44:18 -0500 Subject: [PATCH] adding go mod for v3 --- .gitignore | 1 + go.mod | 9 + go.sum | 24 + v3/base_interface.go | 160 + v3/go.mod | 10 + v3/go.sum | 26 + v3/helpers/eventwebhook/eventwebhook.go | 83 + v3/helpers/eventwebhook/eventwebhook_test.go | 113 + v3/helpers/inbound/inbound.go | 256 ++ v3/helpers/inbound/inbound_test.go | 180 + v3/helpers/inbound/sample_data/bad_data.txt | 1 + .../inbound/sample_data/default_data.txt | 58 + .../default_data_with_attachments.txt | 0 v3/helpers/inbound/sample_data/raw_data.txt | 57 + .../sample_data/raw_data_with_attachments.txt | 298 ++ v3/helpers/mail/mail_v3.go | 757 ++++ v3/helpers/mail/mail_v3_test.go | 838 ++++ v3/sendgrid.go | 49 + v3/sendgrid_test.go | 3733 +++++++++++++++++ v3/twilio_email.go | 41 + v3/twilio_email_test.go | 34 + 21 files changed, 6728 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 v3/base_interface.go create mode 100644 v3/go.mod create mode 100644 v3/go.sum create mode 100644 v3/helpers/eventwebhook/eventwebhook.go create mode 100644 v3/helpers/eventwebhook/eventwebhook_test.go create mode 100644 v3/helpers/inbound/inbound.go create mode 100644 v3/helpers/inbound/inbound_test.go create mode 100644 v3/helpers/inbound/sample_data/bad_data.txt create mode 100644 v3/helpers/inbound/sample_data/default_data.txt create mode 100644 v3/helpers/inbound/sample_data/default_data_with_attachments.txt create mode 100644 v3/helpers/inbound/sample_data/raw_data.txt create mode 100644 v3/helpers/inbound/sample_data/raw_data_with_attachments.txt create mode 100644 v3/helpers/mail/mail_v3.go create mode 100644 v3/helpers/mail/mail_v3_test.go create mode 100644 v3/sendgrid.go create mode 100644 v3/sendgrid_test.go create mode 100644 v3/twilio_email.go create mode 100644 v3/twilio_email_test.go diff --git a/.gitignore b/.gitignore index aac0e6fe..882928f2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ sendgrid.env .vscode prism* **/.idea/**/* +profile.out diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..50df55a9 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/sendgrid/sendgrid-go + +go 1.14 + +require ( + github.com/sendgrid/rest v2.6.9+incompatible + github.com/stretchr/testify v1.8.0 + golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..a3ff1239 --- /dev/null +++ b/go.sum @@ -0,0 +1,24 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= +github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= +golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v3/base_interface.go b/v3/base_interface.go new file mode 100644 index 00000000..257fec14 --- /dev/null +++ b/v3/base_interface.go @@ -0,0 +1,160 @@ +package sendgrid + +import ( + "context" + "errors" + "net/http" + "strconv" + "time" + + "github.com/sendgrid/rest" + "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +// Version is this client library's current version +const ( + Version = "3.11.1" + rateLimitRetry = 5 + rateLimitSleep = 1100 +) + +type options struct { + Auth string + Endpoint string + Host string + Subuser string +} + +// Client is the Twilio SendGrid Go client +type Client struct { + rest.Request +} + +func (o *options) baseURL() string { + return o.Host + o.Endpoint +} + +// requestNew create Request +// @return [Request] a default request object +func requestNew(options options) rest.Request { + requestHeaders := map[string]string{ + "Authorization": options.Auth, + "User-Agent": "sendgrid/" + Version + ";go", + "Accept": "application/json", + } + + if len(options.Subuser) != 0 { + requestHeaders["On-Behalf-Of"] = options.Subuser + } + + return rest.Request{ + BaseURL: options.baseURL(), + Headers: requestHeaders, + } +} + +// Send sends an email through Twilio SendGrid +func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) { + return cl.SendWithContext(context.Background(), email) +} + +// SendWithContext sends an email through Twilio SendGrid with context.Context. +func (cl *Client) SendWithContext(ctx context.Context, email *mail.SGMailV3) (*rest.Response, error) { + cl.Body = mail.GetRequestBody(email) + return MakeRequestWithContext(ctx, cl.Request) +} + +// DefaultClient is used if no custom HTTP client is defined +var DefaultClient = rest.DefaultClient + +// API sets up the request to the Twilio SendGrid API, this is main interface. +// Please use the MakeRequest or MakeRequestAsync functions instead. +// (deprecated) +func API(request rest.Request) (*rest.Response, error) { + return MakeRequest(request) +} + +// MakeRequest attempts a Twilio SendGrid request synchronously. +func MakeRequest(request rest.Request) (*rest.Response, error) { + return MakeRequestWithContext(context.Background(), request) +} + +// MakeRequestWithContext attempts a Twilio SendGrid request synchronously with context.Context. +func MakeRequestWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) { + return DefaultClient.SendWithContext(ctx, request) +} + +// MakeRequestRetry a synchronous request, but retry in the event of a rate +// limited response. +func MakeRequestRetry(request rest.Request) (*rest.Response, error) { + return MakeRequestRetryWithContext(context.Background(), request) +} + +// MakeRequestRetryWithContext a synchronous request with context.Context, but retry in the event of a rate +// limited response. +func MakeRequestRetryWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) { + retry := 0 + var response *rest.Response + var err error + + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + response, err = MakeRequestWithContext(ctx, request) + if err != nil { + return nil, err + } + + if response.StatusCode != http.StatusTooManyRequests { + return response, nil + } + + if retry > rateLimitRetry { + return nil, errors.New("rate limit retry exceeded") + } + retry++ + + resetTime := time.Now().Add(rateLimitSleep * time.Millisecond) + + reset, ok := response.Headers["X-RateLimit-Reset"] + if ok && len(reset) > 0 { + t, err := strconv.Atoi(reset[0]) + if err == nil { + resetTime = time.Unix(int64(t), 0) + } + } + time.Sleep(resetTime.Sub(time.Now())) + } +} + +// MakeRequestAsync attempts a request asynchronously in a new go +// routine. This function returns two channels: responses +// and errors. This function will retry in the case of a +// rate limit. +func MakeRequestAsync(request rest.Request) (chan *rest.Response, chan error) { + return MakeRequestAsyncWithContext(context.Background(), request) +} + +// MakeRequestAsyncWithContext attempts a request asynchronously in a new go +// routine with context.Context. This function returns two channels: responses +// and errors. This function will retry in the case of a +// rate limit. +func MakeRequestAsyncWithContext(ctx context.Context, request rest.Request) (chan *rest.Response, chan error) { + r := make(chan *rest.Response) + e := make(chan error) + + go func() { + response, err := MakeRequestRetryWithContext(ctx, request) + if err != nil { + e <- err + } + if response != nil { + r <- response + } + }() + + return r, e +} diff --git a/v3/go.mod b/v3/go.mod new file mode 100644 index 00000000..a2762f02 --- /dev/null +++ b/v3/go.mod @@ -0,0 +1,10 @@ +module github.com/sendgrid/sendgrid-go/v3 + +go 1.14 + +require ( + github.com/sendgrid/rest v2.6.9+incompatible + github.com/sendgrid/sendgrid-go v3.11.1+incompatible + github.com/stretchr/testify v1.8.0 + golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect +) diff --git a/v3/go.sum b/v3/go.sum new file mode 100644 index 00000000..cf3610b6 --- /dev/null +++ b/v3/go.sum @@ -0,0 +1,26 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= +github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sendgrid/sendgrid-go v3.11.1+incompatible h1:ai0+woZ3r/+tKLQExznak5XerOFoD6S7ePO0lMV8WXo= +github.com/sendgrid/sendgrid-go v3.11.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= +golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v3/helpers/eventwebhook/eventwebhook.go b/v3/helpers/eventwebhook/eventwebhook.go new file mode 100644 index 00000000..9254efc1 --- /dev/null +++ b/v3/helpers/eventwebhook/eventwebhook.go @@ -0,0 +1,83 @@ +package eventwebhook + +import ( + "crypto/ecdsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/json" + "math/big" +) + +const ( + // VerificationHTTPHeader is the signature verification http header name for the signature being sent + VerificationHTTPHeader = "X-Twilio-Email-Event-Webhook-Signature" + // TimestampHTTPHeader is the timestamp http header name for timestamp + TimestampHTTPHeader = "X-Twilio-Email-Event-Webhook-Timestamp" +) + +// Settings ... +type Settings struct { + EnableSignedWebhook *bool `json:"enabled,omitempty"` +} + +// RS represents the ECDSA signature +type RS struct { + R *big.Int + S *big.Int +} + +// NewSettings ... +func NewSettings() *Settings { + return &Settings{} +} + +// SetEnableSignedWebhook ... +func (s *Settings) SetEnableSignedWebhook(enable bool) { + s.EnableSignedWebhook = &enable +} + +// GetRequestBody ... +func GetRequestBody(s *Settings) ([]byte, error) { + b, err := json.Marshal(s) + if err != nil { + return nil, err + } + return b, nil +} + +// ConvertPublicKeyBase64ToECDSA takes a base64 ECDSA public key and converts it into the ECDSA Public Key type +func ConvertPublicKeyBase64ToECDSA(base64PublicKey string) (*ecdsa.PublicKey, error) { + pk, err := base64.StdEncoding.DecodeString(base64PublicKey) + if err != nil { + return nil, err + } + + publicKey, err := x509.ParsePKIXPublicKey(pk) + if err != nil { + return nil, err + } + + return publicKey.(*ecdsa.PublicKey), nil +} + +// VerifySignature uses the ECDSA publicKey and verifies received payload and signature +func VerifySignature(publicKey *ecdsa.PublicKey, payload []byte, signature, timestamp string) (bool, error) { + signatureBytes, err := base64.StdEncoding.DecodeString(signature) + if err != nil { + return false, err + } + + ecdsaSig := &RS{} + _, err = asn1.Unmarshal(signatureBytes, ecdsaSig) + if err != nil { + return false, err + } + + hash := sha256.New() + hash.Write([]byte(timestamp)) + hash.Write(payload) + + return ecdsa.Verify(publicKey, hash.Sum(nil), ecdsaSig.R, ecdsaSig.S), nil +} diff --git a/v3/helpers/eventwebhook/eventwebhook_test.go b/v3/helpers/eventwebhook/eventwebhook_test.go new file mode 100644 index 00000000..8dda5ad6 --- /dev/null +++ b/v3/helpers/eventwebhook/eventwebhook_test.go @@ -0,0 +1,113 @@ +package eventwebhook + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE83T4O/n84iotIvIW4mdBgQ/7dAfSmpqIM8kF9mN1flpVKS3GRqe62gw+2fNNRaINXvVpiglSI8eNEc6wEA3F+g==" + signature = "MEUCIGHQVtGj+Y3LkG9fLcxf3qfI10QysgDWmMOVmxG0u6ZUAiEAyBiXDWzM+uOe5W0JuG+luQAbPIqHh89M15TluLtEZtM=" + timestamp = "1600112502" +) + +func generateTestPayload() []byte { + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + _ = encoder.Encode([]map[string]interface{}{ + { + "email": "hello@world.com", + "event": "dropped", + "reason": "Bounced Address", + "sg_event_id": "ZHJvcC0xMDk5NDkxOS1MUnpYbF9OSFN0T0doUTRrb2ZTbV9BLTA", + "sg_message_id": "LRzXl_NHStOGhQ4kofSm_A.filterdrecv-p3mdw1-756b745b58-kmzbl-18-5F5FC76C-9.0", + "smtp-id": "", + "timestamp": 1600112492, + }, + }) + payload := buffer.Bytes() + payload = payload[:len(payload)-1] // Drop the trailing newline the encoder adds. + payload = append(payload, []byte("\r\n")...) // Append the expected trailing carriage return and newline! + return payload +} + +func TestEventWebhookNewSettings(t *testing.T) { + assert.NotNil(t, NewSettings(), "NewSettings() shouldn't return nil") +} + +func TestSetEnableSignedWebhook(t *testing.T) { + s := NewSettings() + assert.NotNil(t, NewSettings(), "NewSettings() shouldn't return nil") + + s.SetEnableSignedWebhook(true) + assert.Equal(t, true, *s.EnableSignedWebhook, fmt.Sprintf("EnableSignedWebhook should be 'true', got %v", *s.EnableSignedWebhook)) + + s.SetEnableSignedWebhook(false) + assert.Equal(t, false, *s.EnableSignedWebhook, fmt.Sprintf("EnableSignedWebhook should be 'false', got %v", *s.EnableSignedWebhook)) +} + +func TestSignedWebhookGetRequestBody(t *testing.T) { + expectedJSONEnabled := []byte(`{"enabled":true}`) + expectedJSONDisabled := []byte(`{"enabled":false}`) + + s := NewSettings() + assert.NotNil(t, NewSettings(), "NewSettings() shouldn't return nil") + + s.SetEnableSignedWebhook(false) + actualJSON, err := GetRequestBody(s) + require.NoError(t, err) + assert.Equal(t, expectedJSONDisabled, actualJSON, fmt.Sprintf("EnableSignedWebhook should be '%b', got %b", expectedJSONDisabled, actualJSON)) + + s.SetEnableSignedWebhook(true) + actualJSON, err = GetRequestBody(s) + require.NoError(t, err) + assert.Equal(t, expectedJSONEnabled, actualJSON, fmt.Sprintf("EnableSignedWebhook should be '%b', got %b", expectedJSONEnabled, actualJSON)) +} + +func TestConvertPublicKeyBase64ToECDSA(t *testing.T) { + ecdsaKey, err := ConvertPublicKeyBase64ToECDSA(publicKey) + require.NoError(t, err) + assert.NotNil(t, ecdsaKey, "publicKey shouldn't be nil") + + ecdsaKey, err = ConvertPublicKeyBase64ToECDSA(publicKey + "corrupting the public key") + require.Error(t, err) + assert.Nil(t, ecdsaKey, "publicKey should be nil") +} + +func TestVerifySignature(t *testing.T) { + ecdsaKey, err := ConvertPublicKeyBase64ToECDSA(publicKey) + require.NoError(t, err) + + payload := generateTestPayload() + + // verifications + verified, err := VerifySignature(ecdsaKey, payload, signature, timestamp) + require.NoError(t, err) + assert.True(t, verified) + + // not valid payload + verified, err = VerifySignature(ecdsaKey, []byte("this is not valid payload for the given signature"), signature, timestamp) + require.NoError(t, err) + assert.False(t, verified) + + // not valid signature + verified, err = VerifySignature(ecdsaKey, payload, signature+"causing failure", timestamp) + require.Error(t, err) + assert.False(t, verified) + + // not valid timestamp + verified, err = VerifySignature(ecdsaKey, payload, signature, "invalid timestamp") + require.NoError(t, err) + assert.False(t, verified) + + // empty timestamp + verified, err = VerifySignature(ecdsaKey, payload, signature, "") + require.NoError(t, err) + assert.False(t, verified) +} diff --git a/v3/helpers/inbound/inbound.go b/v3/helpers/inbound/inbound.go new file mode 100644 index 00000000..3c688fca --- /dev/null +++ b/v3/helpers/inbound/inbound.go @@ -0,0 +1,256 @@ +package inbound + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime" + "mime/multipart" + "net/http" + "strings" +) + +// ParsedEmail defines a multipart parsed email +// Body and Attachments are only populated if the Raw option is checked on the SendGrid inbound configuration and are named for backwards compatability +type ParsedEmail struct { + // Header values are raw and not pre-processed by SendGrid. They may change depending on the email client. Use carefully + Headers map[string]string + // Please see https://docs.sendgrid.com/for-developers/parsing-email/setting-up-the-inbound-parse-webhook to see the available fields in the email headers + // all fields listed there are available within the headers map except for text which lives in the TextBody field + ParsedValues map[string]string + // Primary email body parsed with \n. A common approach is to Split by the \n to bring every line of the email into a string array + TextBody string + + // Envelope expresses the exact email address that the email was addressed to and the exact email address it was from, without extra characters + Envelope struct { + From string `json:"from"` + To []string `json:"to"` + } + + // Attachments have been fully parsed to include the filename, size, content type and actual file for uploading or processing + ParsedAttachments map[string]*EmailAttachment + + // Raw only + Attachments map[string][]byte + // accessed with text/html and text/plain. text/plain is always parsed to the TextBody field + Body map[string]string + + rawRequest *http.Request + rawValues map[string][]string + withAttachments bool +} + +// EmailAttachment defines information related to an email attachment +type EmailAttachment struct { + File multipart.File `json:"-"` + Filename string `json:"filename"` + Size int64 `json:"-"` + ContentType string `json:"type"` +} + +func newParsedEmail(request *http.Request) ParsedEmail { + return ParsedEmail{ + Headers: make(map[string]string), + ParsedValues: make(map[string]string), + ParsedAttachments: make(map[string]*EmailAttachment), + + Body: make(map[string]string), + Attachments: make(map[string][]byte), + + rawRequest: request, + withAttachments: false, + } +} + +// Parse parses an email using Go's multipart parser and populates the headers, and body +// This method skips processing the attachment file and is therefore more performant +func Parse(request *http.Request) (*ParsedEmail, error) { + result := newParsedEmail(request) + + err := result.parse() + return &result, err +} + +// ParseWithAttachments parses an email using Go's multipart parser and populates the headers, body and processes attachments +func ParseWithAttachments(request *http.Request) (*ParsedEmail, error) { + result := newParsedEmail(request) + result.withAttachments = true + + err := result.parse() + return &result, err +} + +func (email *ParsedEmail) parse() error { + err := email.rawRequest.ParseMultipartForm(0) + if err != nil { + return err + } + + email.rawValues = email.rawRequest.MultipartForm.Value + + // unmarshal the envelope + if len(email.rawValues["envelope"]) > 0 { + if err := json.Unmarshal([]byte(email.rawValues["envelope"][0]), &email.Envelope); err != nil { + return err + } + } + + // parse included headers + if len(email.rawValues["headers"]) > 0 { + email.parseHeaders(email.rawValues["headers"][0]) + } + + // apply the rest of the SendGrid fields to the headers map + for k, v := range email.rawValues { + if k == "text" || k == "email" || k == "headers" || k == "envelope" { + continue + } + + if len(v) > 0 { + email.ParsedValues[k] = v[0] + } + } + + // apply the plain text body + if len(email.rawValues["text"]) > 0 { + email.TextBody = email.rawValues["text"][0] + } + + // only included if the raw box is checked + if len(email.rawValues["email"]) > 0 { + email.parseRawEmail(email.rawValues["email"][0]) + } + + // if the client chose not to parse attachments, return as is + if !email.withAttachments { + return nil + } + + return email.parseAttachments(email.rawValues) +} + +func (email *ParsedEmail) parseAttachments(values map[string][]string) error { + if len(values["attachment-info"]) != 1 { + return nil + } + // unmarshal the sendgrid parsed aspects of the email attachment into the attachment struct + if err := json.Unmarshal([]byte(values["attachment-info"][0]), &email.ParsedAttachments); err != nil { + return err + } + + // range through the multipart files + for key, val := range email.rawRequest.MultipartForm.File { + // open the attachment file for processing + file, err := val[0].Open() + if err != nil { + return err + } + + // add the actual file and the size to the parsed files + email.ParsedAttachments[key].File = file + email.ParsedAttachments[key].Size = val[0].Size + + // if the file does not have a name. give it Untitled + if email.ParsedAttachments[key].Filename == "" { + email.ParsedAttachments[key].Filename = "Untitled" + } + } + + return nil +} + +func (email *ParsedEmail) parseRawEmail(rawEmail string) error { + sections := strings.SplitN(rawEmail, "\n\n", 2) + email.parseHeaders(sections[0]) + raw, err := parseMultipart(strings.NewReader(sections[1]), email.Headers["Content-Type"]) + if err != nil { + return err + } + + for { + emailPart, err := raw.NextPart() + if err == io.EOF { + return nil + } + rawEmailBody, err := parseMultipart(emailPart, emailPart.Header.Get("Content-Type")) + if err != nil { + return err + } + if rawEmailBody != nil { + for { + emailBodyPart, err := rawEmailBody.NextPart() + if err == io.EOF { + break + } + header := emailBodyPart.Header.Get("Content-Type") + b, err := ioutil.ReadAll(emailPart) + if err != nil { + return err + } + email.Body[header] = string(b) + } + + } else if emailPart.FileName() != "" { + b, err := ioutil.ReadAll(emailPart) + if err != nil { + return err + } + email.Attachments[emailPart.FileName()] = b + } else { + header := emailPart.Header.Get("Content-Type") + b, err := ioutil.ReadAll(emailPart) + if err != nil { + return err + } + + email.Body[header] = string(b) + } + } +} + +func parseMultipart(body io.Reader, contentType string) (*multipart.Reader, error) { + mediaType, params, err := mime.ParseMediaType(contentType) + if err != nil { + return nil, err + } + + if strings.HasPrefix(mediaType, "multipart/") { + return multipart.NewReader(body, params["boundary"]), nil + } + return nil, nil +} + +func (email *ParsedEmail) parseHeaders(headers string) { + splitHeaders := strings.Split(strings.TrimSpace(headers), "\n") + for _, header := range splitHeaders { + splitHeader := strings.SplitN(header, ": ", 2) + // keeps outlook emails from causing a panic + if len(splitHeader) != 2 { + continue + } + + email.Headers[splitHeader[0]] = splitHeader[1] + } +} + +// Validate validates the DKIM and SPF scores to ensure that the email client and address was not spoofed +func (email *ParsedEmail) Validate() error { + if len(email.rawValues["dkim"]) == 0 || len(email.rawValues["SPF"]) == 0 { + return fmt.Errorf("missing DKIM and SPF score") + } + + for _, val := range email.rawValues["dkim"] { + if !strings.Contains(val, "pass") { + return fmt.Errorf("DKIM validation failed") + } + } + + for _, val := range email.rawValues["SPF"] { + if !strings.Contains(val, "pass") { + return fmt.Errorf("SPF validation failed") + } + } + + return nil +} diff --git a/v3/helpers/inbound/inbound_test.go b/v3/helpers/inbound/inbound_test.go new file mode 100644 index 00000000..d776f767 --- /dev/null +++ b/v3/helpers/inbound/inbound_test.go @@ -0,0 +1,180 @@ +package inbound + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func createRequest(filename string) *http.Request { + file, err := ioutil.ReadFile(filename) + if err != nil { + return nil + } + + // Build POST request + req, _ := http.NewRequest(http.MethodPost, "", bytes.NewReader(file)) + req.Header.Set("Content-Type", "multipart/form-data; boundary=xYzZY") + req.Header.Set("User-Agent", "Twilio-SendGrid-Test") + return req +} + +func TestParse(t *testing.T) { + // Build a table of tests to run with each one having a name, the sample data file to post, + // and the expected HTTP response from the handler + tests := []struct { + name string + file string + expectedError error + }{ + { + name: "NoAttachment", + file: "./sample_data/raw_data.txt", + }, + { + name: "Attachment", + file: "./sample_data/raw_data_with_attachments.txt", + }, + { + name: "DefaultData", + file: "./sample_data/default_data.txt", + }, + { + name: "BadData", + file: "./sample_data/bad_data.txt", + expectedError: fmt.Errorf("multipart: NextPart: EOF"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(subTest *testing.T) { + //Load POST body + req := createRequest(test.file) + + // Invoke callback handler + email, err := Parse(req) + if test.expectedError != nil { + assert.Error(subTest, err, "expected an error to occur") + return + } + + assert.NoError(subTest, err, "did NOT expect an error to occur") + + from := "Example User " + assert.Equalf(subTest, email.Headers["From"], from, "Expected From: %s, Got: %s", from, email.Headers["From"]) + }) + } +} + +func ExampleParsedEmail_parseHeaders() { + headers := ` +Foo: foo +Bar: baz +` + email := ParsedEmail{ + Headers: make(map[string]string), + Body: make(map[string]string), + Attachments: make(map[string][]byte), + rawRequest: nil, + } + email.parseHeaders(headers) + fmt.Println(email.Headers["Foo"]) + fmt.Println(email.Headers["Bar"]) + // Output: + // foo + // baz +} + +func ExampleParsedEmail_parseRawEmail() { + rawEmail := ` +To: test@example.com +From: example@example.com +Subject: Test Email +Content-Type: multipart/mixed; boundary=TwiLIo + +--TwiLIo +Content-Type: text/plain; charset=UTF-8 + +Hello Twilio SendGrid! +--TwiLIo +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hello Twilio SendGrid! +--TwiLIo-- +` + email := ParsedEmail{ + Headers: make(map[string]string), + Body: make(map[string]string), + Attachments: make(map[string][]byte), + rawRequest: nil, + } + + if err := email.parseRawEmail(rawEmail); err != nil { + log.Fatal(err) + } + + for key, value := range email.Headers { + fmt.Println(key, value) + } + fmt.Println(email.Body["text/plain; charset=UTF-8"]) + // Unordered Output: + // To test@example.com + // From example@example.com + // Subject Test Email + // Content-Type multipart/mixed; boundary=TwiLIo + // Hello Twilio SendGrid! +} + +func TestValidate(t *testing.T) { + tests := []struct { + name string + values map[string][]string + expectedError error + }{ + { + name: "MissingHeaders", + values: map[string][]string{}, + expectedError: fmt.Errorf("missing DKIM and SPF score"), + }, + { + name: "FailedDkim", + values: map[string][]string{"dkim": {"pass", "fail", "pass"}, "SPF": {"pass"}}, + expectedError: fmt.Errorf("DKIM validation failed"), + }, + { + name: "FailedSpf", + values: map[string][]string{"dkim": {"pass", "pass", "pass"}, "SPF": {"pass", "fail", "pass"}}, + expectedError: fmt.Errorf("SPF validation failed"), + }, + { + name: "FailedSpfandDkim", + values: map[string][]string{"dkim": {"pass", "pass", "fail"}, "SPF": {"pass", "fail", "pass"}}, + expectedError: fmt.Errorf("DKIM validation failed"), + }, + { + name: "success", + values: map[string][]string{"dkim": {"pass", "pass", "pass"}, "SPF": {"pass", "pass", "pass"}}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(subTest *testing.T) { + //Load POST body + email := ParsedEmail{rawValues: test.values} + err := email.Validate() + + if test.expectedError != nil { + assert.EqualError(subTest, test.expectedError, err.Error()) + return + } + + assert.NoError(subTest, err, "did NOT expect an error to occur") + }) + } +} diff --git a/v3/helpers/inbound/sample_data/bad_data.txt b/v3/helpers/inbound/sample_data/bad_data.txt new file mode 100644 index 00000000..c5540c80 --- /dev/null +++ b/v3/helpers/inbound/sample_data/bad_data.txt @@ -0,0 +1 @@ +this is bad invalid data \ No newline at end of file diff --git a/v3/helpers/inbound/sample_data/default_data.txt b/v3/helpers/inbound/sample_data/default_data.txt new file mode 100644 index 00000000..7c3ce6be --- /dev/null +++ b/v3/helpers/inbound/sample_data/default_data.txt @@ -0,0 +1,58 @@ +--xYzZY +Content-Disposition: form-data; name="headers" + +MIME-Version: 1.0 +Received: by 0.0.0.0 with HTTP; Wed, 10 Aug 2016 18:10:13 -0700 (PDT) +From: Example User +Date: Wed, 10 Aug 2016 18:10:13 -0700 +Subject: Inbound Parse Test Data +To: inbound@inbound.example.com +Content-Type: multipart/alternative; boundary=001a113df448cad2d00539c16e89 + +--xYzZY +Content-Disposition: form-data; name="dkim" + +{@sendgrid.com : pass} +--xYzZY +Content-Disposition: form-data; name="to" + +inbound@inbound.example.com +--xYzZY +Content-Disposition: form-data; name="html" + +Hello Twilio SendGrid! + +--xYzZY +Content-Disposition: form-data; name="from" + +Example User +--xYzZY +Content-Disposition: form-data; name="text" + +Hello Twilio SendGrid! + +--xYzZY +Content-Disposition: form-data; name="sender_ip" + +0.0.0.0 +--xYzZY +Content-Disposition: form-data; name="envelope" + +{"to":["inbound@inbound.example.com"],"from":"test@example.com"} +--xYzZY +Content-Disposition: form-data; name="attachments" + +0 +--xYzZY +Content-Disposition: form-data; name="subject" + +Testing non-raw +--xYzZY +Content-Disposition: form-data; name="charsets" + +{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"} +--xYzZY +Content-Disposition: form-data; name="SPF" + +pass +--xYzZY-- \ No newline at end of file diff --git a/v3/helpers/inbound/sample_data/default_data_with_attachments.txt b/v3/helpers/inbound/sample_data/default_data_with_attachments.txt new file mode 100644 index 00000000..e69de29b diff --git a/v3/helpers/inbound/sample_data/raw_data.txt b/v3/helpers/inbound/sample_data/raw_data.txt new file mode 100644 index 00000000..12f64cb4 --- /dev/null +++ b/v3/helpers/inbound/sample_data/raw_data.txt @@ -0,0 +1,57 @@ +--xYzZY +Content-Disposition: form-data; name="dkim" + +{@sendgrid.com : pass} +--xYzZY +Content-Disposition: form-data; name="email" + +MIME-Version: 1.0 +Received: by 0.0.0.0 with HTTP; Wed, 10 Aug 2016 14:44:21 -0700 (PDT) +From: Example User +Date: Wed, 10 Aug 2016 14:44:21 -0700 +Subject: Inbound Parse Test Raw Data +To: inbound@inbound.inbound.com +Content-Type: multipart/alternative; boundary=001a113ee97c89842f0539be8e7a + +--001a113ee97c89842f0539be8e7a +Content-Type: text/plain; charset=UTF-8 + +Hello Twilio SendGrid! + +--001a113ee97c89842f0539be8e7a +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hello Twilio SendGrid! + +--001a113ee97c89842f0539be8e7a-- + +--xYzZY +Content-Disposition: form-data; name="to" + +inbound@inbound.inbound.com +--xYzZY +Content-Disposition: form-data; name="from" + +Example User +--xYzZY +Content-Disposition: form-data; name="sender_ip" + +0.0.0.0 +--xYzZY +Content-Disposition: form-data; name="envelope" + +{"to":["inbound@inbound.inbound.com"],"from":"test@example.com"} +--xYzZY +Content-Disposition: form-data; name="subject" + +Testing with Request.bin +--xYzZY +Content-Disposition: form-data; name="charsets" + +{"to":"UTF-8","subject":"UTF-8","from":"UTF-8"} +--xYzZY +Content-Disposition: form-data; name="SPF" + +pass +--xYzZY-- \ No newline at end of file diff --git a/v3/helpers/inbound/sample_data/raw_data_with_attachments.txt b/v3/helpers/inbound/sample_data/raw_data_with_attachments.txt new file mode 100644 index 00000000..657ddf8e --- /dev/null +++ b/v3/helpers/inbound/sample_data/raw_data_with_attachments.txt @@ -0,0 +1,298 @@ +--xYzZY +Content-Disposition: form-data; name="dkim" + +{@sendgrid.com : pass} +--xYzZY +Content-Disposition: form-data; name="email" + +MIME-Version: 1.0 +Received: by 0.0.0.0 with HTTP; Mon, 15 Aug 2016 13:47:21 -0700 (PDT) +From: Example User +Date: Mon, 15 Aug 2016 13:47:21 -0700 +Subject: Inbound Parse Test Raw Data with Attachment +To: inbound@inbound.inbound.com +Content-Type: multipart/mixed; boundary=001a1140ffb6f4fc63053a2257e2 + +--001a1140ffb6f4fc63053a2257e2 +Content-Type: multipart/alternative; boundary=001a1140ffb6f4fc5f053a2257e0 + +--001a1140ffb6f4fc5f053a2257e0 +Content-Type: text/plain; charset=UTF-8 + +Hello Twilio SendGrid! + +--001a1140ffb6f4fc5f053a2257e0 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hello Twilio SendGrid! + +--001a1140ffb6f4fc5f053a2257e0-- + +--001a1140ffb6f4fc63053a2257e2 +Content-Type: image/jpeg; name="TwilioSendGrid.jpg" +Content-Disposition: attachment; filename="TwilioSendGrid.jpg" +Content-Transfer-Encoding: base64 +X-Attachment-Id: f_irwihell0 + +/9j/4AAQSkZJRgABAQABLAEsAAD/4QDKRXhpZgAATU0AKgAAAAgABwESAAMA +AAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAEx +AAIAAAARAAAAcgEyAAIAAAAUAAAAhIdpAAQAAAABAAAAmAAAAAAAAAEsAAAA +AQAAASwAAAABUGl4ZWxtYXRvciAzLjQuNAAAMjAxNjowODoxMSAxNjowODo1 +NwAAA6ABAAMAAAABAAEAAKACAAQAAAABAAACEqADAAQAAAABAAACFQAAAAD/ +4Qn2aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVn +aW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4 +bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAg +Q29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53 +My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3Jp +cHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2Jl +LmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9l +bGVtZW50cy8xLjEvIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxNi0wOC0xMVQxNjow +ODo1NyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBJbWFnZVJlYWR5Ij4gPGRj +OnN1YmplY3Q+IDxyZGY6QmFnLz4gPC9kYzpzdWJqZWN0PiA8L3JkZjpEZXNj +cmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3Ag +My4wADhCSU0EBAAAAAAAADhCSU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/ +wAARCAIVAhIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQF +BgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJx +FDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdI +SUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj +pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx +8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QA +tREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB +CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldY +WVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq +srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6 +/9sAQwAcHBwcHBwwHBwwRDAwMERcRERERFx0XFxcXFx0jHR0dHR0dIyMjIyM +jIyMqKioqKioxMTExMTc3Nzc3Nzc3Nzc/9sAQwEiJCQ4NDhgNDRg5pyAnObm +5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm +5ubm/90ABAAi/9oADAMBAAIRAxEAPwDpKKKKACiiigAooooAKKKKACiiigAo +oooAKKKKACiiigAooooAKKKKACiiigDEnkkEzgMep71F5sn94/nTp/8AXP8A +U1FXWloeZJu7H+bJ/eP50ebJ/eP50yinYm7H+bJ/eP50ebJ/eP50yiiwXY/z +ZP7x/OjzZP7x/OmUUWC7H+bJ/eP50ebJ/eP50yiiwXY/zZP7x/OjzZP7x/Om +UUWC7H+bJ/eP50ebJ/eP50yiiwXY/wA2T+8fzo82T+8fzplFFgux/myf3j+d +Hmyf3j+dMoosF2P82T+8fzo82T+8fzplFFgux/myf3j+dHmyf3j+dMoosF2I +0sufvt+dJ5sv99vzNMbrSVVkbpuxJ5sv99vzNHmy/wB9vzNR0U7Id2SebL/f +b8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/32/M1HRRZBdknmy/32/M0ebL/fb8 +zUdFFkF2SebL/fb8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/32/M1HRRZBdknm +y/32/M0ebL/fb8zUdFFkF2SebL/fb8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/ +32/M1HRRZBdknmy/32/M0ebL/fb8zUdFFkF2SebL/fb8zR5sv99vzNR0UWQX +Z//Q6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo +oooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKK +KKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKAC +iiigAooooAKKKKACiiigAooooAKKKKACiiigD//R6SiiigAooooAKKKKACii +igAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKu +xbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig +CJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo +oAKKKKACiiigD//S6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA +KKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKAC +iiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiig +AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//T6SiiigAo +oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1N +RVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooo +oAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKK +KKACiiigAooooAKKKKACiiigD//U6SiiigAooooAKKKKACiiigAooooAKKKK +ACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUy +QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3 +WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig +D//V6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo +oooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKK +KKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKAC +iiigAooooAKKKKACiiigAooooAKKKKACiiigD//W6SiiigAooooAKKKKACii +igAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKu +xbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig +CJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo +oAKKKKACiiigD//X6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA +KKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKAC +iiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiig +AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q6SiiigAo +oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1N +RVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooo +oAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKK +KKACiiigAooooAKKKKACiiigD//R6SiiigAooooAKKKKACiiigAooooAKKKK +ACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUy +QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3 +WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig +D//S2i7560m9/WmnqaStbHPdj97+tG9/WmUUWC7H739aN7+tMoosF2P3v60b +39aZRRYLsfvf1o3v60yiiwXY/e/rRvf1plFFgux+9/Wje/rTKKLBdj97+tG9 +/WmUUWC7H739aN7+tMoosF2P3v60b39aZRRYLsfvf1o3v60yiiwXZlzEmVvr +UeTT5f8AWt9ajrdbHI9xcmjJpKKYhcmjJpKKAFyaMmkooAXJoyaSigBcmjJp +KKAFyaMmkooAXJoyaSigBcmjJpKKAFyaMmkooAXJoyaSigBwAPWlwKB0paBX +YmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAX +YmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXZ//9PXPU0l +KeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK +AMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFF +FABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAoooo +AKKKKACiiigAooooAKKKKACiiigD/9TXPU0lKeppK1OYKKKKACiiigAooooA +KKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyv +cKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQ +dKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi +iigD/9XXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii +igAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABR +RRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKK +KACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9bXPU0lKeppK1OYKKKK +ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqS +X/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU +AFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA +ooooAKKKKACiiigD/9fXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAo +oooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQA +UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACi +iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9DXPU0l +KeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK +AMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFF +FABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAoooo +AKKKKACiiigAooooAKKKKACiiigD/9HXPU0lKeppK1OYKKKKACiiigAooooA +KKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyv +cKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQ +dKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi +iigD/9LXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii +igAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABR +RRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKK +KACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9PXPU0lKeppK1OYKKKK +ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqS +X/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU +AFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA +ooooAKKKKACiiigD/9TXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAo +oooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQA +UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACi +iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9WV7mUO +Rnv6U37TN6/pUMn32+pptdyiuxxNssfaZvX9KPtM3r+lV6KOVdguyx9pm9f0 +o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXYLssfaZvX9KPtM3r+lV6KOV +dguyx9pm9f0o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXYLssfaZvX9KP +tM3r+lV6KOVdguyx9pm9f0o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXY +LssfaZvX9KPtM3r+lV6KOVdguzRSFJFEjdTyad9mi96fD/ql+lS1g27lcqK/ +2aL3o+zRe9WKKXMw5V2K/wBmi96Ps0XvViijmYcq7Ff7NF70fZoverFFHMw5 +V2K/2aL3o+zRe9WKKOZhyrsV/s0XvR9mi96sUUczDlXYr/Zovej7NF71Yoo5 +mHKuxX+zRe9H2aL3qxRRzMOVdiv9mi96Ps0XvViijmYcq7Ff7NF70fZoverF +FHMw5V2K/wBmi96Ps0XvViijmYcq7BHZwlcnP50/7FB6H86ni+4KkqHJ9zdU +422Kn2KD0P50fYoPQ/nVuilzvuP2cexU+xQeh/Oj7FB6H86t0Uc77h7OPYqf +YoPQ/nR9ig9D+dW6KOd9w9nHsVPsUHofzo+xQeh/OrdFHO+4ezj2Kn2KD0P5 +0fYoPQ/nVuijnfcPZx7FT7FB6H86PsUHofzq3RRzvuHs49ip9ig9D+dH2KD0 +P51boo533D2cexU+xQeh/Oj7FB6H86t0Uc77h7OPYqfYoPQ/nR9ig9D+dW6K +Od9w9nHsVPsUHofzo+xQeh/OrdFHO+4ezj2Kn2KD0P50fYoPQ/nVuijnfcPZ +x7H/1mSffb6mm06T77fU02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAo +oooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFAB +RRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooo +pDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9dk +n32+pptOk++31NNrvRwsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii +igAooooAKKKKANaH/VL9KlqKH/VL9Klrme5qgooopAFFFFABRRRQAUUUUAFF +FFABRRRQAUUUUAFFFFABRRRQAUUUUAWovuCpKji+4KkrN7nRHYKKKKQwoooo +AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//QZJ99vqab +TpPvt9TTa70cLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK +ACiiigDWh/1S/Spaih/1S/Spa5nuaoKKKKQBRRRQAUUUUAFFFFABRRRQAUUU +UAFFFFABRRRQAUUUUAFFFFAFqL7gqSo4vuCpKze50R2CiiikMKKKKACiiigA +ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/0WSffb6mm06T77fU +02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA +1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQ +AUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooopDCiiigAooooAKKKKACi +iigAooooAKKKKACiiigAooooAKKKKACiiigD/9Jkn32+pptOk++31NNrvRws +KKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKANaH/VL9 +KlqKH/VL9Klrme5qgooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAB +RRRQAUUUUAWovuCpKji+4KkrN7nRHYKKKKQwooooAKKKKACiiigAooooAKKK +KACiiigAooooAKKKKACiiigAooooA//TZJ99vqabTpPvt9TTa70cLCiiigAo +oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDWh/1S/Spaih/1 +S/Spa5nuaoKKKKQBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFF +FFAFqL7gqSo4vuCpKze50R2CiiikMKKKKACiiigAooooAKKKKACiiigAoooo +AKKKKACiiigAooooAKKKKAP/1GSffb6mm06T77fU02u9HCwooooAKKKKACii +igAooooAKKKKACiiigAooooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ +7mqCiiikAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+ +4KkqOL7gqSs3udEdgooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigA +ooooAKKKKACiiigD/9Vkn32+pptOk++31NNrvRwsKKKKACiiigAooooAKKKK +ACiiigAooooAKKKKACiiigAooooAKKKKANaH/VL9KlqKH/VL9Klrme5qgooo +pAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAWovuCpKji+ +4KkrN7nRHYKKKKQwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi +iigAooooA//WZJ99vqabTpPvt9TTa70cLCiiigAooooAKKKKACiiigAooooA +KKKKACiiigAooooAKKKKACiiigDWh/1S/Spaih/1S/Spa5nuaoKKKKQBRRRQ +AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFqL7gqSo4vuCpKze5 +0R2CiiikMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKK +KAP/12Sffb6mm06T77fU02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAo +oooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFAB +RRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooo +pDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9DU +OnxMSdx5pP7Oi/vNWjRV+0l3I5I9jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s +6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+8 +1H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9n +Rf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3m +rRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo +9pLuHs49iBLdUUKCeKd5K+pqWip5mPlRF5K+po8lfU1LRRdhyoi8lfU0eSvq +aloouw5UReSvqaPJX1NS0UXYcqIvJX1NHkr6mpaKLsOVEXkr6mjyV9TUtFF2 +HKiLyV9TR5K+pqWii7DlRF5K+po8lfU1LRRdhyoi8lfU0eSvqaloouw5UReS +vqaPJX1NS0UXYcqIvJX1NHkr6mpaKLsOVCKu0YFLRRSKCiiigAooooAKKKKA +CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9k= + +--001a1140ffb6f4fc63053a2257e2-- + +--xYzZY +Content-Disposition: form-data; name="to" + +inbound@inbound.inbound.com +--xYzZY +Content-Disposition: form-data; name="from" + +Example User +--xYzZY +Content-Disposition: form-data; name="sender_ip" + +0.0.0.0 +--xYzZY +Content-Disposition: form-data; name="envelope" + +{"to":["inbound@inbound.inbound.com"],"from":"test@example.com"} +--xYzZY +Content-Disposition: form-data; name="subject" + +Raw Payload +--xYzZY +Content-Disposition: form-data; name="charsets" + +{"to":"UTF-8","subject":"UTF-8","from":"UTF-8"} +--xYzZY +Content-Disposition: form-data; name="SPF" + +pass +--xYzZY-- \ No newline at end of file diff --git a/v3/helpers/mail/mail_v3.go b/v3/helpers/mail/mail_v3.go new file mode 100644 index 00000000..6d7ac6b4 --- /dev/null +++ b/v3/helpers/mail/mail_v3.go @@ -0,0 +1,757 @@ +package mail + +import ( + "encoding/json" + "fmt" + "log" + "net/mail" + "strings" +) + +const ( + // RFC 3696 ( https://tools.ietf.org/html/rfc3696#section-3 ) + // The domain part (after the "@") must not exceed 255 characters + maxEmailDomainLength = 255 + // The "local part" (before the "@") must not exceed 64 characters + maxEmailLocalLength = 64 + // Max email length must not exceed 320 characters. + maxEmailLength = maxEmailDomainLength + maxEmailLocalLength + 1 +) + +// SGMailV3 contains mail struct +type SGMailV3 struct { + From *Email `json:"from,omitempty"` + Subject string `json:"subject,omitempty"` + Personalizations []*Personalization `json:"personalizations,omitempty"` + Content []*Content `json:"content,omitempty"` + Attachments []*Attachment `json:"attachments,omitempty"` + TemplateID string `json:"template_id,omitempty"` + Sections map[string]string `json:"sections,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Categories []string `json:"categories,omitempty"` + CustomArgs map[string]string `json:"custom_args,omitempty"` + SendAt int `json:"send_at,omitempty"` + BatchID string `json:"batch_id,omitempty"` + Asm *Asm `json:"asm,omitempty"` + IPPoolID string `json:"ip_pool_name,omitempty"` + MailSettings *MailSettings `json:"mail_settings,omitempty"` + TrackingSettings *TrackingSettings `json:"tracking_settings,omitempty"` + ReplyTo *Email `json:"reply_to,omitempty"` +} + +// Personalization holds mail body struct +type Personalization struct { + To []*Email `json:"to,omitempty"` + From *Email `json:"from,omitempty"` + CC []*Email `json:"cc,omitempty"` + BCC []*Email `json:"bcc,omitempty"` + Subject string `json:"subject,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Substitutions map[string]string `json:"substitutions,omitempty"` + CustomArgs map[string]string `json:"custom_args,omitempty"` + DynamicTemplateData map[string]interface{} `json:"dynamic_template_data,omitempty"` + Categories []string `json:"categories,omitempty"` + SendAt int `json:"send_at,omitempty"` +} + +// Email holds email name and address info +type Email struct { + Name string `json:"name,omitempty"` + Address string `json:"email,omitempty"` +} + +// Content defines content of the mail body +type Content struct { + Type string `json:"type,omitempty"` + Value string `json:"value,omitempty"` +} + +// Attachment holds attachement information +type Attachment struct { + Content string `json:"content,omitempty"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Filename string `json:"filename,omitempty"` + Disposition string `json:"disposition,omitempty"` + ContentID string `json:"content_id,omitempty"` +} + +// Asm contains Grpip Id and int array of groups ID +type Asm struct { + GroupID int `json:"group_id,omitempty"` + GroupsToDisplay []int `json:"groups_to_display,omitempty"` +} + +// MailSettings defines mail and spamCheck settings +type MailSettings struct { + BCC *BccSetting `json:"bcc,omitempty"` + BypassListManagement *Setting `json:"bypass_list_management,omitempty"` + BypassSpamManagement *Setting `json:"bypass_spam_management,omitempty"` + BypassBounceManagement *Setting `json:"bypass_bounce_management,omitempty"` + BypassUnsubscribeManagement *Setting `json:"bypass_unsubscribe_management,omitempty"` + Footer *FooterSetting `json:"footer,omitempty"` + SandboxMode *Setting `json:"sandbox_mode,omitempty"` + SpamCheckSetting *SpamCheckSetting `json:"spam_check,omitempty"` +} + +// TrackingSettings holds tracking settings and mail settings +type TrackingSettings struct { + ClickTracking *ClickTrackingSetting `json:"click_tracking,omitempty"` + OpenTracking *OpenTrackingSetting `json:"open_tracking,omitempty"` + SubscriptionTracking *SubscriptionTrackingSetting `json:"subscription_tracking,omitempty"` + GoogleAnalytics *GaSetting `json:"ganalytics,omitempty"` + BCC *BccSetting `json:"bcc,omitempty"` + BypassListManagement *Setting `json:"bypass_list_management,omitempty"` + Footer *FooterSetting `json:"footer,omitempty"` + SandboxMode *SandboxModeSetting `json:"sandbox_mode,omitempty"` +} + +// BccSetting holds email bcc setings to enable of disable +// default is false +type BccSetting struct { + Enable *bool `json:"enable,omitempty"` + Email string `json:"email,omitempty"` +} + +// FooterSetting holds enaable/disable settings +// and the format of footer i.e HTML/Text +type FooterSetting struct { + Enable *bool `json:"enable,omitempty"` + Text string `json:"text,omitempty"` + Html string `json:"html,omitempty"` +} + +// ClickTrackingSetting ... +type ClickTrackingSetting struct { + Enable *bool `json:"enable,omitempty"` + EnableText *bool `json:"enable_text,omitempty"` +} + +// OpenTrackingSetting ... +type OpenTrackingSetting struct { + Enable *bool `json:"enable,omitempty"` + SubstitutionTag string `json:"substitution_tag,omitempty"` +} + +// SandboxModeSetting ... +type SandboxModeSetting struct { + Enable *bool `json:"enable,omitempty"` + ForwardSpam *bool `json:"forward_spam,omitempty"` + SpamCheck *SpamCheckSetting `json:"spam_check,omitempty"` +} + +// SpamCheckSetting holds spam settings and +// which can be enable or disable and +// contains spamThreshold value +type SpamCheckSetting struct { + Enable *bool `json:"enable,omitempty"` + SpamThreshold int `json:"threshold,omitempty"` + PostToURL string `json:"post_to_url,omitempty"` +} + +// SubscriptionTrackingSetting ... +type SubscriptionTrackingSetting struct { + Enable *bool `json:"enable,omitempty"` + Text string `json:"text,omitempty"` + Html string `json:"html,omitempty"` + SubstitutionTag string `json:"substitution_tag,omitempty"` +} + +// GaSetting ... +type GaSetting struct { + Enable *bool `json:"enable,omitempty"` + CampaignSource string `json:"utm_source,omitempty"` + CampaignTerm string `json:"utm_term,omitempty"` + CampaignContent string `json:"utm_content,omitempty"` + CampaignName string `json:"utm_campaign,omitempty"` + CampaignMedium string `json:"utm_medium,omitempty"` +} + +// Setting enables the mail settings +type Setting struct { + Enable *bool `json:"enable,omitempty"` +} + +// NewV3Mail ... +func NewV3Mail() *SGMailV3 { + return &SGMailV3{ + Personalizations: make([]*Personalization, 0), + Content: make([]*Content, 0), + Attachments: make([]*Attachment, 0), + } +} + +// NewV3MailInit ... +func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3 { + m := new(SGMailV3) + m.SetFrom(from) + m.Subject = subject + p := NewPersonalization() + p.AddTos(to) + m.AddPersonalizations(p) + m.AddContent(content...) + return m +} + +// GetRequestBody ... +func GetRequestBody(m *SGMailV3) []byte { + b, err := json.Marshal(m) + if err != nil { + log.Println(err) + } + return b +} + +// AddPersonalizations ... +func (s *SGMailV3) AddPersonalizations(p ...*Personalization) *SGMailV3 { + s.Personalizations = append(s.Personalizations, p...) + return s +} + +// AddContent ... +func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3 { + s.Content = append(s.Content, c...) + return s +} + +// AddAttachment ... +func (s *SGMailV3) AddAttachment(a ...*Attachment) *SGMailV3 { + s.Attachments = append(s.Attachments, a...) + return s +} + +// SetFrom ... +func (s *SGMailV3) SetFrom(e *Email) *SGMailV3 { + s.From = e + return s +} + +// SetReplyTo ... +func (s *SGMailV3) SetReplyTo(e *Email) *SGMailV3 { + s.ReplyTo = e + return s +} + +// SetTemplateID ... +func (s *SGMailV3) SetTemplateID(templateID string) *SGMailV3 { + s.TemplateID = templateID + return s +} + +// AddSection ... +func (s *SGMailV3) AddSection(key string, value string) *SGMailV3 { + if s.Sections == nil { + s.Sections = make(map[string]string) + } + + s.Sections[key] = value + return s +} + +// SetHeader ... +func (s *SGMailV3) SetHeader(key string, value string) *SGMailV3 { + if s.Headers == nil { + s.Headers = make(map[string]string) + } + + s.Headers[key] = value + return s +} + +// AddCategories ... +func (s *SGMailV3) AddCategories(category ...string) *SGMailV3 { + s.Categories = append(s.Categories, category...) + return s +} + +// SetCustomArg ... +func (s *SGMailV3) SetCustomArg(key string, value string) *SGMailV3 { + if s.CustomArgs == nil { + s.CustomArgs = make(map[string]string) + } + + s.CustomArgs[key] = value + return s +} + +// SetSendAt ... +func (s *SGMailV3) SetSendAt(sendAt int) *SGMailV3 { + s.SendAt = sendAt + return s +} + +// SetBatchID ... +func (s *SGMailV3) SetBatchID(batchID string) *SGMailV3 { + s.BatchID = batchID + return s +} + +// SetASM ... +func (s *SGMailV3) SetASM(asm *Asm) *SGMailV3 { + s.Asm = asm + return s +} + +// SetIPPoolID ... +func (s *SGMailV3) SetIPPoolID(ipPoolID string) *SGMailV3 { + s.IPPoolID = ipPoolID + return s +} + +// SetMailSettings ... +func (s *SGMailV3) SetMailSettings(mailSettings *MailSettings) *SGMailV3 { + s.MailSettings = mailSettings + return s +} + +// SetTrackingSettings ... +func (s *SGMailV3) SetTrackingSettings(trackingSettings *TrackingSettings) *SGMailV3 { + s.TrackingSettings = trackingSettings + return s +} + +// NewPersonalization ... +func NewPersonalization() *Personalization { + return &Personalization{ + To: make([]*Email, 0), + CC: make([]*Email, 0), + BCC: make([]*Email, 0), + Headers: make(map[string]string), + Substitutions: make(map[string]string), + CustomArgs: make(map[string]string), + DynamicTemplateData: make(map[string]interface{}), + Categories: make([]string, 0), + } +} + +// AddTos ... +func (p *Personalization) AddTos(to ...*Email) { + p.To = append(p.To, to...) +} + +// AddFrom ... +func (p *Personalization) AddFrom(from *Email) { + p.From = from +} + +// AddCCs ... +func (p *Personalization) AddCCs(cc ...*Email) { + p.CC = append(p.CC, cc...) +} + +// AddBCCs ... +func (p *Personalization) AddBCCs(bcc ...*Email) { + p.BCC = append(p.BCC, bcc...) +} + +// SetHeader ... +func (p *Personalization) SetHeader(key string, value string) { + p.Headers[key] = value +} + +// SetSubstitution ... +func (p *Personalization) SetSubstitution(key string, value string) { + p.Substitutions[key] = value +} + +// SetCustomArg ... +func (p *Personalization) SetCustomArg(key string, value string) { + p.CustomArgs[key] = value +} + +// SetDynamicTemplateData ... +func (p *Personalization) SetDynamicTemplateData(key string, value interface{}) { + p.DynamicTemplateData[key] = value +} + +// SetSendAt ... +func (p *Personalization) SetSendAt(sendAt int) { + p.SendAt = sendAt +} + +// NewAttachment ... +func NewAttachment() *Attachment { + return &Attachment{} +} + +// SetContent ... +func (a *Attachment) SetContent(content string) *Attachment { + a.Content = content + return a +} + +// SetType ... +func (a *Attachment) SetType(contentType string) *Attachment { + a.Type = contentType + return a +} + +// SetFilename ... +func (a *Attachment) SetFilename(filename string) *Attachment { + a.Filename = filename + return a +} + +// SetDisposition ... +func (a *Attachment) SetDisposition(disposition string) *Attachment { + a.Disposition = disposition + return a +} + +// SetContentID ... +func (a *Attachment) SetContentID(contentID string) *Attachment { + a.ContentID = contentID + return a +} + +// NewASM ... +func NewASM() *Asm { + return &Asm{} +} + +// SetGroupID ... +func (a *Asm) SetGroupID(groupID int) *Asm { + a.GroupID = groupID + return a +} + +// AddGroupsToDisplay ... +func (a *Asm) AddGroupsToDisplay(groupsToDisplay ...int) *Asm { + a.GroupsToDisplay = append(a.GroupsToDisplay, groupsToDisplay...) + return a +} + +// NewMailSettings ... +func NewMailSettings() *MailSettings { + return &MailSettings{} +} + +// SetBCC ... +func (m *MailSettings) SetBCC(bcc *BccSetting) *MailSettings { + m.BCC = bcc + return m +} + +// SetBypassListManagement ... +func (m *MailSettings) SetBypassListManagement(bypassListManagement *Setting) *MailSettings { + m.BypassListManagement = bypassListManagement + return m +} + +// SetBypassSpamManagement ... +func (m *MailSettings) SetBypassSpamManagement(bypassSpamManagement *Setting) *MailSettings { + m.BypassSpamManagement = bypassSpamManagement + return m +} + +// SetBypassBounceManagement ... +func (m *MailSettings) SetBypassBounceManagement(bypassBounceManagement *Setting) *MailSettings { + m.BypassBounceManagement = bypassBounceManagement + return m +} + +// SetBypassUnsubscribeManagement ... +func (m *MailSettings) SetBypassUnsubscribeManagement(bypassUnsubscribeManagement *Setting) *MailSettings { + m.BypassUnsubscribeManagement = bypassUnsubscribeManagement + return m +} + +// SetFooter ... +func (m *MailSettings) SetFooter(footerSetting *FooterSetting) *MailSettings { + m.Footer = footerSetting + return m +} + +// SetSandboxMode ... +func (m *MailSettings) SetSandboxMode(sandboxMode *Setting) *MailSettings { + m.SandboxMode = sandboxMode + return m +} + +// SetSpamCheckSettings ... +func (m *MailSettings) SetSpamCheckSettings(spamCheckSetting *SpamCheckSetting) *MailSettings { + m.SpamCheckSetting = spamCheckSetting + return m +} + +// NewTrackingSettings ... +func NewTrackingSettings() *TrackingSettings { + return &TrackingSettings{} +} + +// SetClickTracking ... +func (t *TrackingSettings) SetClickTracking(clickTracking *ClickTrackingSetting) *TrackingSettings { + t.ClickTracking = clickTracking + return t + +} + +// SetOpenTracking ... +func (t *TrackingSettings) SetOpenTracking(openTracking *OpenTrackingSetting) *TrackingSettings { + t.OpenTracking = openTracking + return t +} + +// SetSubscriptionTracking ... +func (t *TrackingSettings) SetSubscriptionTracking(subscriptionTracking *SubscriptionTrackingSetting) *TrackingSettings { + t.SubscriptionTracking = subscriptionTracking + return t +} + +// SetGoogleAnalytics ... +func (t *TrackingSettings) SetGoogleAnalytics(googleAnalytics *GaSetting) *TrackingSettings { + t.GoogleAnalytics = googleAnalytics + return t +} + +// NewBCCSetting ... +func NewBCCSetting() *BccSetting { + return &BccSetting{} +} + +// SetEnable ... +func (b *BccSetting) SetEnable(enable bool) *BccSetting { + setEnable := enable + b.Enable = &setEnable + return b +} + +// SetEmail ... +func (b *BccSetting) SetEmail(email string) *BccSetting { + b.Email = email + return b +} + +// NewFooterSetting ... +func NewFooterSetting() *FooterSetting { + return &FooterSetting{} +} + +// SetEnable ... +func (f *FooterSetting) SetEnable(enable bool) *FooterSetting { + setEnable := enable + f.Enable = &setEnable + return f +} + +// SetText ... +func (f *FooterSetting) SetText(text string) *FooterSetting { + f.Text = text + return f +} + +// SetHTML ... +func (f *FooterSetting) SetHTML(html string) *FooterSetting { + f.Html = html + return f +} + +// NewOpenTrackingSetting ... +func NewOpenTrackingSetting() *OpenTrackingSetting { + return &OpenTrackingSetting{} +} + +// SetEnable ... +func (o *OpenTrackingSetting) SetEnable(enable bool) *OpenTrackingSetting { + setEnable := enable + o.Enable = &setEnable + return o +} + +// SetSubstitutionTag ... +func (o *OpenTrackingSetting) SetSubstitutionTag(subTag string) *OpenTrackingSetting { + o.SubstitutionTag = subTag + return o +} + +// NewSubscriptionTrackingSetting ... +func NewSubscriptionTrackingSetting() *SubscriptionTrackingSetting { + return &SubscriptionTrackingSetting{} +} + +// SetEnable ... +func (s *SubscriptionTrackingSetting) SetEnable(enable bool) *SubscriptionTrackingSetting { + setEnable := enable + s.Enable = &setEnable + return s +} + +// SetText ... +func (s *SubscriptionTrackingSetting) SetText(text string) *SubscriptionTrackingSetting { + s.Text = text + return s +} + +// SetHTML ... +func (s *SubscriptionTrackingSetting) SetHTML(html string) *SubscriptionTrackingSetting { + s.Html = html + return s +} + +// SetSubstitutionTag ... +func (s *SubscriptionTrackingSetting) SetSubstitutionTag(subTag string) *SubscriptionTrackingSetting { + s.SubstitutionTag = subTag + return s +} + +// NewGaSetting ... +func NewGaSetting() *GaSetting { + return &GaSetting{} +} + +// SetEnable ... +func (g *GaSetting) SetEnable(enable bool) *GaSetting { + setEnable := enable + g.Enable = &setEnable + return g +} + +// SetCampaignSource ... +func (g *GaSetting) SetCampaignSource(campaignSource string) *GaSetting { + g.CampaignSource = campaignSource + return g +} + +// SetCampaignContent ... +func (g *GaSetting) SetCampaignContent(campaignContent string) *GaSetting { + g.CampaignContent = campaignContent + return g +} + +// SetCampaignTerm ... +func (g *GaSetting) SetCampaignTerm(campaignTerm string) *GaSetting { + g.CampaignTerm = campaignTerm + return g +} + +// SetCampaignName ... +func (g *GaSetting) SetCampaignName(campaignName string) *GaSetting { + g.CampaignName = campaignName + return g +} + +// SetCampaignMedium ... +func (g *GaSetting) SetCampaignMedium(campaignMedium string) *GaSetting { + g.CampaignMedium = campaignMedium + return g +} + +// NewSetting ... +func NewSetting(enable bool) *Setting { + setEnable := enable + return &Setting{Enable: &setEnable} +} + +// NewEmail ... +func NewEmail(name string, address string) *Email { + return &Email{ + Name: name, + Address: address, + } +} + +// NewSingleEmail ... +func NewSingleEmail(from *Email, subject string, to *Email, plainTextContent string, htmlContent string) *SGMailV3 { + var contents []*Content + if plainTextContent != "" { + contents = append(contents, NewContent("text/plain", plainTextContent)) + } + if htmlContent != "" { + contents = append(contents, NewContent("text/html", htmlContent)) + } + return NewV3MailInit(from, subject, to, contents...) +} + +// NewSingleEmailPlainText is used to build *SGMailV3 object having only 'plain-text' as email content. +func NewSingleEmailPlainText(from *Email, subject string, to *Email, plainTextContent string) *SGMailV3 { + plainText := NewContent("text/plain", plainTextContent) + return NewV3MailInit(from, subject, to, plainText) +} + +// NewContent ... +func NewContent(contentType string, value string) *Content { + return &Content{ + Type: contentType, + Value: value, + } +} + +// NewClickTrackingSetting ... +func NewClickTrackingSetting() *ClickTrackingSetting { + return &ClickTrackingSetting{} +} + +// SetEnable ... +func (c *ClickTrackingSetting) SetEnable(enable bool) *ClickTrackingSetting { + setEnable := enable + c.Enable = &setEnable + return c +} + +// SetEnableText ... +func (c *ClickTrackingSetting) SetEnableText(enableText bool) *ClickTrackingSetting { + setEnable := enableText + c.EnableText = &setEnable + return c +} + +// NewSpamCheckSetting ... +func NewSpamCheckSetting() *SpamCheckSetting { + return &SpamCheckSetting{} +} + +// SetEnable ... +func (s *SpamCheckSetting) SetEnable(enable bool) *SpamCheckSetting { + setEnable := enable + s.Enable = &setEnable + return s +} + +// SetSpamThreshold ... +func (s *SpamCheckSetting) SetSpamThreshold(spamThreshold int) *SpamCheckSetting { + s.SpamThreshold = spamThreshold + return s +} + +// SetPostToURL ... +func (s *SpamCheckSetting) SetPostToURL(postToURL string) *SpamCheckSetting { + s.PostToURL = postToURL + return s +} + +// NewSandboxModeSetting ... +func NewSandboxModeSetting(enable bool, forwardSpam bool, spamCheck *SpamCheckSetting) *SandboxModeSetting { + setEnable := enable + setForwardSpam := forwardSpam + return &SandboxModeSetting{ + Enable: &setEnable, + ForwardSpam: &setForwardSpam, + SpamCheck: spamCheck, + } +} + +// ParseEmail parses a string that contains an rfc822 formatted email address +// and returns an instance of *Email. +func ParseEmail(emailInfo string) (*Email, error) { + e, err := mail.ParseAddress(emailInfo) + if err != nil { + return nil, err + } + + if len(e.Address) > maxEmailLength { + return nil, fmt.Errorf("Invalid email length. Total length should not exceed %d characters.", maxEmailLength) + } + + parts := strings.Split(e.Address, "@") + local, domain := parts[0], parts[1] + + if len(domain) > maxEmailDomainLength { + return nil, fmt.Errorf("Invalid email length. Domain length should not exceed %d characters.", maxEmailDomainLength) + } + + if len(local) > maxEmailLocalLength { + return nil, fmt.Errorf("Invalid email length. Local part length should not exceed %d characters.", maxEmailLocalLength) + } + + return NewEmail(e.Name, e.Address), nil +} diff --git a/v3/helpers/mail/mail_v3_test.go b/v3/helpers/mail/mail_v3_test.go new file mode 100644 index 00000000..28af9192 --- /dev/null +++ b/v3/helpers/mail/mail_v3_test.go @@ -0,0 +1,838 @@ +package mail + +import ( + "encoding/json" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// TestV3NewMail will test New mail method +func TestV3NewMail(t *testing.T) { + m := NewV3Mail() + + assert.NotNil(t, m, "NewV3Mail() shouldn't return nil") + assert.NotNil(t, m.Personalizations, "Personalizations shouldn't be nil") + assert.NotNil(t, m.Attachments, "Attachments shouldn't be nil") + assert.NotNil(t, m.Content, "Content shouldn't be nil") +} + +func TestV3NewMailInit(t *testing.T) { + from := NewEmail("Example User", "test@example.com") + subject := "Hello World from the Twilio SendGrid Go Library" + to := NewEmail("Example User", "test@example.com") + content := NewContent("text/plain", "some text here") + m := NewV3MailInit(from, subject, to, content) + + assert.NotNil(t, m, "NewV3MailInit() shouldn't return nil") + assert.NotNil(t, m.From, "From shouldn't be nil") + assert.NotNil(t, m.Personalizations, "Personalizations shouldn't be nil") + assert.NotNil(t, m.Content, "Content shouldn't be nil") +} + +func TestV3AddPersonalizations(t *testing.T) { + numOfPersonalizations := rand.New(rand.NewSource(99)).Intn(10) + personalizations := make([]*Personalization, 0) + for i := 0; i < numOfPersonalizations; i++ { + personalizations = append(personalizations, NewPersonalization()) + } + + m := NewV3Mail() + m.AddPersonalizations(personalizations...) + + assert.Equal(t, numOfPersonalizations, len(m.Personalizations), fmt.Sprintf("Mail should have %d personalizations, got %d personalizations", len(personalizations), len(m.Personalizations))) +} + +func TestV3AddContent(t *testing.T) { + numOfContent := 2 + content := make([]*Content, 0) + for i := 0; i < numOfContent; i++ { + content = append(content, NewContent("type", "value")) + } + + m := NewV3Mail() + m.AddContent(content...) + + assert.Equal(t, numOfContent, len(m.Content), fmt.Sprintf("Mail should have %d contents, got %d contents", numOfContent, len(m.Content))) +} + +func TestV3AddAttachment(t *testing.T) { + numOfAttachments := 2 + attachment := make([]*Attachment, 0) + for i := 0; i < numOfAttachments; i++ { + attachment = append(attachment, NewAttachment()) + } + + m := NewV3Mail() + m.AddAttachment(attachment...) + + assert.Equal(t, numOfAttachments, len(m.Attachments), fmt.Sprintf("Mail should have %d attachments, got %d attachments", numOfAttachments, len(m.Attachments))) +} + +func TestV3SetFrom(t *testing.T) { + m := NewV3Mail() + + address := "test@example.com" + name := "Test User" + e := NewEmail(name, address) + m.SetFrom(e) + + assert.Equal(t, name, m.From.Name, fmt.Sprintf("name should be %s, got %s", name, e.Name)) + assert.Equal(t, address, m.From.Address, fmt.Sprintf("address should be %s, got %s", address, e.Address)) +} + +func TestV3SetReplyTo(t *testing.T) { + m := NewV3Mail() + + address := "test@example.com" + name := "Test User" + e := NewEmail(name, address) + m.SetReplyTo(e) + + assert.Equal(t, name, m.ReplyTo.Name, fmt.Sprintf("name should be %s, got %s", name, e.Name)) + assert.Equal(t, address, m.ReplyTo.Address, fmt.Sprintf("address should be %s, got %s", address, e.Address)) +} + +func TestV3SetTemplateID(t *testing.T) { + m := NewV3Mail() + + templateID := "templateabcd12345" + + m.SetTemplateID(templateID) + + assert.Equal(t, templateID, m.TemplateID, fmt.Sprintf("templateID should be %s, got %s", templateID, m.TemplateID)) +} + +func TestV3AddSection(t *testing.T) { + m := NewV3Mail() + + sectionKey := "key" + sectionValue := "value" + + m.AddSection(sectionKey, sectionValue) + + v, ok := m.Sections[sectionKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in Sections map", sectionKey)) { + assert.Equal(t, v, sectionValue, fmt.Sprintf("value should be %s, got %s", sectionValue, v)) + } +} + +func TestV3SetHeader(t *testing.T) { + m := NewV3Mail() + + headerKey := "key" + headerValue := "value" + + m.SetHeader(headerKey, headerValue) + + v, ok := m.Headers[headerKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in Headers map", headerKey)) { + assert.Equal(t, v, headerValue, fmt.Sprintf("value should be %s, got %s", headerValue, v)) + } +} + +func TestV3AddCategory(t *testing.T) { + m := NewV3Mail() + + categories := []string{"cats", "dogs", "hamburgers", "cheeseburgers"} + + m.AddCategories(categories...) + + assert.Equal(t, len(categories), len(m.Categories), fmt.Sprintf("Length of Categories should be %d, got %d", len(categories), len(m.Categories))) +} + +func TestV3SetCustomArg(t *testing.T) { + m := NewV3Mail() + + customArgKey := "key" + customArgValue := "value" + + m.SetCustomArg(customArgKey, customArgValue) + + v, ok := m.CustomArgs[customArgKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in CustomArgs map", customArgKey)) { + assert.Equal(t, v, customArgValue, fmt.Sprintf("value should be %s, got %s", customArgValue, v)) + } +} + +func TestV3SetSendAt(t *testing.T) { + m := NewV3Mail() + sendAt := time.Now().Second() + + m.SetSendAt(sendAt) + assert.Equal(t, sendAt, m.SendAt, fmt.Sprintf("SendAt should be %d, got %d", sendAt, m.SendAt)) +} + +func TestV3SetBatchID(t *testing.T) { + m := NewV3Mail() + batchID := "batchID123455" + + m.SetBatchID(batchID) + assert.Equal(t, batchID, m.BatchID, fmt.Sprintf("BatchID should be %s, got %s", batchID, m.BatchID)) +} + +func TestV3SetIPPoolID(t *testing.T) { + m := NewV3Mail() + ipPoolID := "42" + + m.SetIPPoolID(ipPoolID) + assert.Equal(t, ipPoolID, m.IPPoolID, fmt.Sprintf("IP Pool ID should be %s, got %s", ipPoolID, m.IPPoolID)) +} + +func TestV3SetASM(t *testing.T) { + m := NewV3Mail() + asm := NewASM() + groupID := 1 + groupsToDisplay := []int{1, 2, 3, 4} + asm.SetGroupID(groupID) + asm.AddGroupsToDisplay(groupsToDisplay...) + + m.SetASM(asm) + + assert.Equal(t, groupID, m.Asm.GroupID, fmt.Sprintf("GroupID should be %d, got %d", groupID, m.Asm.GroupID)) + assert.Equal(t, groupsToDisplay, m.Asm.GroupsToDisplay, fmt.Sprintf("Length of GroupsToDisplay should be %d, got %d", len(groupsToDisplay), len(m.Asm.GroupsToDisplay))) +} + +func TestV3SetMailSettings(t *testing.T) { + m := NewV3Mail() + ms := NewMailSettings() + ms.SetBCC(NewBCCSetting().SetEnable(true)) + m.SetMailSettings(ms) + + assert.NotNil(t, m.MailSettings, "Mail Settings should not be nil") + assert.True(t, *m.MailSettings.BCC.Enable, "BCC should be enabled in Mail Settings") +} + +func TestV3SetTrackingSettings(t *testing.T) { + m := NewV3Mail() + ts := NewTrackingSettings() + n := NewClickTrackingSetting() + n.SetEnable(true) + n.SetEnableText(true) + ts.SetClickTracking(n) + m.SetTrackingSettings(ts) + + assert.NotNil(t, m.TrackingSettings, "Tracking Settings should not be nil") + assert.True(t, *m.TrackingSettings.ClickTracking.Enable, "Click Tracking should be enabled") +} + +func TestV3NewPersonalization(t *testing.T) { + p := NewPersonalization() + + assert.NotNil(t, p, "NewPersonalization() shouldn't return nil") + + assert.NotNil(t, p.To, "To should't be nil") + assert.Equal(t, 0, len(p.To), "Length of p.To should be 0") + + assert.NotNil(t, p.CC, "CC should't be nil") + assert.Equal(t, 0, len(p.CC), "Length of p.CCs should be 0") + + assert.NotNil(t, p.BCC, "BCC should't be nil") + assert.Equal(t, 0, len(p.BCC), "Length of p.BCC should be 0") + + assert.NotNil(t, p.Headers, "Headers should't be nil") + assert.Equal(t, 0, len(p.Headers), "Length of p.Headers should be 0") + + assert.NotNil(t, p.Substitutions, "Substitutions should't be nil") + assert.Equal(t, 0, len(p.Substitutions), "Length of p.Substitutions should be 0") + + assert.NotNil(t, p.CustomArgs, "CustomArgs should't be nil") + assert.Equal(t, 0, len(p.CustomArgs), "Length of p.CustomArgs should be 0") + + assert.NotNil(t, p.Categories, "Categories should't be nil") + assert.Equal(t, 0, len(p.Categories), "Length of p.Categories should be 0") +} + +func TestV3PersonalizationAddTos(t *testing.T) { + tos := []*Email{ + NewEmail("Example User", "test@example.com"), + NewEmail("Example User", "test@example.com"), + } + + p := NewPersonalization() + p.AddTos(tos...) + + assert.Equal(t, len(tos), len(p.To), fmt.Sprintf("length of To should be %d, got %d", len(tos), len(p.To))) +} + +func TestV3PersonalizationAddFrom(t *testing.T) { + address := "test@example.com" + name := "Test User" + from := NewEmail(name, address) + + p := NewPersonalization() + p.AddFrom(from) + + assert.Equal(t, name, p.From.Name, fmt.Sprintf("name should be %s got %s", name, p.From.Name)) + assert.Equal(t, address, p.From.Address, fmt.Sprintf("address should be %s got %s", address, p.From.Address)) +} + +func TestV3PersonalizationAddCCs(t *testing.T) { + ccs := []*Email{ + NewEmail("Example User", "test@example.com"), + NewEmail("Example User", "test@example.com"), + } + + p := NewPersonalization() + p.AddCCs(ccs...) + + assert.Equal(t, len(ccs), len(p.CC), fmt.Sprintf("length of CC should be %d, got %d", len(ccs), len(p.CC))) +} + +func TestV3PersonalizationAddBCCs(t *testing.T) { + bccs := []*Email{ + NewEmail("Example User", "test@example.com"), + NewEmail("Example User", "test@example.com"), + } + + p := NewPersonalization() + p.AddBCCs(bccs...) + + assert.Equal(t, len(bccs), len(p.BCC), fmt.Sprintf("length of BCC should be %d, got %d", len(bccs), len(p.BCC))) +} + +func TestV3PersonalizationSetHeader(t *testing.T) { + p := NewPersonalization() + + headerKey := "key" + headerValue := "value" + + p.SetHeader(headerKey, headerValue) + + v, ok := p.Headers[headerKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in Headers map", headerKey)) { + assert.Equal(t, headerValue, v, fmt.Sprintf("value should be %s, got %s", headerValue, v)) + } +} + +func TestV3PersonalizationSetSubstitution(t *testing.T) { + p := NewPersonalization() + + substitutionKey := "key" + substitutionValue := "value" + + p.SetSubstitution(substitutionKey, substitutionValue) + + v, ok := p.Substitutions[substitutionKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in Substitutions map", substitutionKey)) { + assert.Equal(t, substitutionValue, v, fmt.Sprintf("value should be %s, got %s", substitutionValue, v)) + } +} + +func TestV3PersonalizationSetCustomArg(t *testing.T) { + p := NewPersonalization() + + customArgKey := "key" + customArgValue := "value" + + p.SetCustomArg(customArgKey, customArgValue) + + v, ok := p.CustomArgs[customArgKey] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in CustomArgs map", customArgKey)) { + assert.Equal(t, customArgValue, v, fmt.Sprintf("value should be %s, got %s", customArgValue, v)) + } +} + +func TestV3PersonalizationSetDynamicTemplateData(t *testing.T) { + p := NewPersonalization() + + dynamicTemplateDataKey0 := "simpleString" + dynamicTemplateDataValue0 := "value" + p.SetDynamicTemplateData(dynamicTemplateDataKey0, dynamicTemplateDataValue0) + + v, ok := p.DynamicTemplateData[dynamicTemplateDataKey0] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in DynamictemplateData map", dynamicTemplateDataKey0)) { + assert.Equal(t, dynamicTemplateDataValue0, v, fmt.Sprintf("value should be %s, got %s", dynamicTemplateDataValue0, v)) + } + + dynamicTemplateDataKey1 := "arr" + dynamicTemplateDataValue1 := "[true, false, true]" + p.SetDynamicTemplateData(dynamicTemplateDataKey1, dynamicTemplateDataValue1) + + v, ok = p.DynamicTemplateData[dynamicTemplateDataKey1] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in DynamictemplateData map", dynamicTemplateDataKey1)) { + assert.Equal(t, dynamicTemplateDataValue1, v, fmt.Sprintf("value should be %s, got %s", dynamicTemplateDataValue1, v)) + } + + dynamicTemplateDataKey2 := "obj" + dynamicTemplateDataValue2 := map[string]string{ + "dynamic": "templates", + "dynamicArr": "[]int{0, 1, 2}", + "bool": "false", + "int": "10", + } + p.SetDynamicTemplateData(dynamicTemplateDataKey2, dynamicTemplateDataValue2) + + v, ok = p.DynamicTemplateData[dynamicTemplateDataKey2] + if !assert.True(t, ok, fmt.Sprintf("key %s not found in DynamictemplateData map", dynamicTemplateDataKey2)) { + assert.Equal(t, dynamicTemplateDataValue2, v, fmt.Sprintf("value should be %s, got %s", dynamicTemplateDataValue2, v)) + } +} + +func TestV3PersonalizationSetSendAt(t *testing.T) { + p := NewPersonalization() + sendAt := time.Now().Second() + + p.SetSendAt(sendAt) + assert.Equal(t, sendAt, p.SendAt, fmt.Sprintf("sendat should be %d, got %d", sendAt, p.SendAt)) +} + +func TestV3NewAttachment(t *testing.T) { + assert.NotNil(t, NewAttachment(), "NewAttachment() shouldn't return nil") +} + +func TestV3AttachmentSetContent(t *testing.T) { + content := "somebase64encodedcontent" + a := NewAttachment().SetContent(content) + + assert.Equal(t, content, a.Content, fmt.Sprintf("Content should be %s, got %s", content, a.Content)) +} + +func TestV3AttachmentSetType(t *testing.T) { + contentType := "pdf" + a := NewAttachment().SetType(contentType) + + assert.Equal(t, contentType, a.Type, fmt.Sprintf("ContentType should be %s, got %s", contentType, a.Type)) +} + +func TestV3AttachmentSetContentID(t *testing.T) { + contentID := "contentID" + a := NewAttachment().SetContentID(contentID) + + assert.Equal(t, contentID, a.ContentID, fmt.Sprintf("ContentID should be %s, got %s", contentID, a.ContentID)) +} + +func TestV3AttachmentSetDisposition(t *testing.T) { + disposition := "inline" + a := NewAttachment().SetDisposition(disposition) + + assert.Equal(t, disposition, a.Disposition, fmt.Sprintf("Disposition should be %s, got %s", disposition, a.Disposition)) +} + +func TestV3AttachmentSetFilename(t *testing.T) { + filename := "mydoc.pdf" + a := NewAttachment().SetFilename(filename) + + assert.Equal(t, filename, a.Filename, fmt.Sprintf("Filename should be %s, got %s", filename, a.Filename)) +} + +func TestV3NewASM(t *testing.T) { + assert.NotNil(t, NewASM(), "NewASM() should not return nil") +} + +func TestV3ASMSetGroupID(t *testing.T) { + groupID := 1 + a := NewASM().SetGroupID(groupID) + + assert.Equal(t, groupID, a.GroupID, fmt.Sprintf("GroupID should be %v, got %v", groupID, a.GroupID)) +} + +func TestV3ASMSetGroupstoDisplay(t *testing.T) { + groupsToDisplay := []int{1, 2, 3, 4} + a := NewASM().AddGroupsToDisplay(groupsToDisplay...) + + assert.Equal(t, len(groupsToDisplay), len(a.GroupsToDisplay), fmt.Sprintf("Length of GroupsToDisplay should be %d, got %d", groupsToDisplay, a.GroupsToDisplay)) +} + +func TestV3NewMailSettings(t *testing.T) { + assert.NotNil(t, NewMailSettings(), "NewMailSettings() shouldn't return nil") +} + +func TestV3MailSettingsSetBCC(t *testing.T) { + m := NewMailSettings().SetBCC(NewBCCSetting().SetEnable(true)) + + assert.NotNil(t, m.BCC, "BCC should not be nil") + assert.True(t, *m.BCC.Enable, "BCC should be enabled") +} + +func TestV3MailSettingsSetBypassListManagement(t *testing.T) { + m := NewMailSettings().SetBypassListManagement(NewSetting(true)) + + assert.NotNil(t, m.BypassListManagement, "BypassListManagement should not be nil") + assert.True(t, *m.BypassListManagement.Enable, "BypassListManagement should be enabled") +} + +func TestV3MailSettingsSetBypassSpamManagement(t *testing.T) { + m := NewMailSettings().SetBypassSpamManagement(NewSetting(true)) + + assert.NotNil(t, m.BypassSpamManagement, "BypassSpamManagement should not be nil") + assert.True(t, *m.BypassSpamManagement.Enable, "BypassSpamManagement should be enabled") +} + +func TestV3MailSettingsSetBypassBounceManagement(t *testing.T) { + m := NewMailSettings().SetBypassBounceManagement(NewSetting(true)) + + assert.NotNil(t, m.BypassBounceManagement, "BypassBounceManagement should not be nil") + assert.True(t, *m.BypassBounceManagement.Enable, "BypassBounceManagement should be enabled") +} + +func TestV3MailSettingsSetBypassUnsubscribeManagement(t *testing.T) { + m := NewMailSettings().SetBypassUnsubscribeManagement(NewSetting(true)) + + assert.NotNil(t, m.BypassUnsubscribeManagement, "BypassUnsubscribeManagement should not be nil") + assert.True(t, *m.BypassUnsubscribeManagement.Enable, "BypassUnsubscribeManagement should be enabled") +} + +func TestV3MailSettingsSetSandboxMode(t *testing.T) { + m := NewMailSettings().SetSandboxMode(NewSetting(true)) + + assert.NotNil(t, m.SandboxMode, "SandboxMode should not be nil") + assert.True(t, *m.SandboxMode.Enable, "SandboxMode should be enabled") +} + +func TestV3MailSettingsSpamCheckSettings(t *testing.T) { + m := NewMailSettings() + s := NewSpamCheckSetting() + s.SetEnable(true) + s.SetPostToURL("http://test.com") + s.SetSpamThreshold(1) + m.SetSpamCheckSettings(s) + + assert.True(t, *m.SpamCheckSetting.Enable, "SpamCheckSetting should be enabled") + assert.NotNil(t, m.SpamCheckSetting.PostToURL, "PostToURL should not be nil") + assert.Equal(t, 1, m.SpamCheckSetting.SpamThreshold, "Spam threshold should be 1") +} + +func TestV3MailSettingsSetFooter(t *testing.T) { + m := NewMailSettings().SetFooter(NewFooterSetting().SetEnable(true)) + + assert.NotNil(t, m.Footer, "Footer should not be nil") + assert.True(t, *m.Footer.Enable, "Footer should be enabled") +} + +func TestV3NewTrackingSettings(t *testing.T) { + assert.NotNil(t, NewTrackingSettings(), "NewTrackingSettings() shouldn't return nil") +} + +func TestV3TrackingSettingsSetClickTracking(t *testing.T) { + n := NewClickTrackingSetting() + n.SetEnable(true) + n.SetEnableText(true) + ts := NewTrackingSettings().SetClickTracking(n) + + assert.NotNil(t, ts.ClickTracking, "Click Tracking shouldn't return nil") + assert.True(t, *ts.ClickTracking.Enable, "ClickTracking should be enabled") +} + +func TestV3TrackingSettingsSetOpenTracking(t *testing.T) { + substitutionTag := "subTag" + ts := NewTrackingSettings().SetOpenTracking(NewOpenTrackingSetting().SetEnable(true).SetSubstitutionTag(substitutionTag)) + + assert.NotNil(t, ts.OpenTracking, "Open Tracking should not be nil") + assert.True(t, *ts.OpenTracking.Enable, "OpenTracking should be enabled") + assert.Equal(t, substitutionTag, ts.OpenTracking.SubstitutionTag, fmt.Sprintf("Substitution Tag should be %s, got %s", substitutionTag, ts.OpenTracking.SubstitutionTag)) +} + +func TestV3TrackingSettingsSetSubscriptionTracking(t *testing.T) { + ts := NewTrackingSettings().SetSubscriptionTracking(NewSubscriptionTrackingSetting()) + assert.NotNil(t, ts.SubscriptionTracking, "SubscriptionTracking should not be nil") +} + +func TestV3TrackingSettingsSetGoogleAnalytics(t *testing.T) { + campaignName := "campaign1" + campaignTerm := "campaign1_term" + campaignSource := "campaign1_source" + campaignContent := "campaign1_content" + campaignMedium := "campaign1_medium" + + ts := NewTrackingSettings().SetGoogleAnalytics(NewGaSetting().SetCampaignName(campaignName).SetCampaignTerm(campaignTerm).SetCampaignSource(campaignSource).SetCampaignContent(campaignContent).SetCampaignMedium(campaignMedium).SetEnable(true)) + + assert.NotNil(t, ts.GoogleAnalytics, "GoogleAnalytics should not be nil") + + assert.Equal(t, campaignName, ts.GoogleAnalytics.CampaignName, fmt.Sprintf("CampaignName should be %s, got %s", campaignName, ts.GoogleAnalytics.CampaignName)) + + assert.Equal(t, campaignTerm, ts.GoogleAnalytics.CampaignTerm, fmt.Sprintf("CampaignTerm should be %s, got %s", campaignTerm, ts.GoogleAnalytics.CampaignTerm)) + + assert.Equal(t, campaignSource, ts.GoogleAnalytics.CampaignSource, fmt.Sprintf("CampaignSource should be %s, got %s", campaignSource, ts.GoogleAnalytics.CampaignSource)) + + assert.Equal(t, campaignContent, ts.GoogleAnalytics.CampaignContent, fmt.Sprintf("CampaignContent should be %s, got %s", campaignContent, ts.GoogleAnalytics.CampaignContent)) + + assert.Equal(t, campaignMedium, ts.GoogleAnalytics.CampaignMedium, fmt.Sprintf("CampaignMedium should be %s, got %s", campaignMedium, ts.GoogleAnalytics.CampaignMedium)) +} + +func TestV3NewBCCSetting(t *testing.T) { + assert.NotNil(t, NewBCCSetting(), "NewBCCSetting() shouldn't return nil") +} + +func TestV3BCCSettingSetEnable(t *testing.T) { + b := NewBCCSetting().SetEnable(true) + + assert.True(t, *b.Enable, "BCCSetting should be enabled") +} + +func TestV3BCCSettingSetEmail(t *testing.T) { + address := "joe@schmoe.net" + b := NewBCCSetting().SetEmail(address) + + assert.NotNil(t, b.Email, "Email should not be empty") +} + +func TestV3NewFooterSetting(t *testing.T) { + assert.NotNil(t, NewFooterSetting(), "NewFooterSetting() shouldn't return nil") +} + +func TestV3FooterSettingSetEnable(t *testing.T) { + f := NewFooterSetting().SetEnable(true) + + assert.True(t, *f.Enable, "FooterSetting should be enabled") +} + +func TestV3FooterSettingSetText(t *testing.T) { + text := "some test here" + f := NewFooterSetting().SetText(text) + + assert.Equal(t, text, f.Text, fmt.Sprintf("Text should be %s, got %s", text, f.Text)) +} + +func TestV3FooterSettingSetHtml(t *testing.T) { + html := "

some html

" + f := NewFooterSetting().SetHTML(html) + + assert.Equal(t, html, f.Html, fmt.Sprintf("Html should be %s, got %s", html, f.Html)) +} + +func TestV3NewOpenTrackingSetting(t *testing.T) { + assert.NotNil(t, NewOpenTrackingSetting(), "NewOpenTrackingSetting() shouldn't return nil") +} + +func TestV3OpenTrackingSettingSetEnable(t *testing.T) { + f := NewOpenTrackingSetting().SetEnable(true) + + assert.True(t, *f.Enable, "OpenTrackingSetting should be enabled") +} + +func TestV3OpenTrackingSettingSetSubstitutionTag(t *testing.T) { + substitutionTag := "tag" + f := NewOpenTrackingSetting().SetSubstitutionTag(substitutionTag) + + assert.Equal(t, substitutionTag, f.SubstitutionTag, fmt.Sprintf("SubstitutionTag should be %s, got %s", substitutionTag, f.SubstitutionTag)) +} + +func TestV3NewSubscriptionTrackingSetting(t *testing.T) { + assert.NotNil(t, NewSubscriptionTrackingSetting(), "NewSubscriptionTrackingSetting() shouldn't return nil") +} + +func TestV3NewSubscriptionTrackingSetEnable(t *testing.T) { + s := NewSubscriptionTrackingSetting().SetEnable(true) + + assert.True(t, *s.Enable, "SubscriptionTracking should be enabled") +} + +func TestV3NewSubscriptionTrackingSetSubstitutionTag(t *testing.T) { + substitutionTag := "subTag" + s := NewSubscriptionTrackingSetting().SetSubstitutionTag(substitutionTag) + + assert.Equal(t, substitutionTag, s.SubstitutionTag, fmt.Sprintf("SubstitutionTag should be %s, got %s", substitutionTag, s.SubstitutionTag)) +} + +func TestV3NewSubscriptionTrackingSetText(t *testing.T) { + text := "text" + s := NewSubscriptionTrackingSetting().SetText(text) + + assert.Equal(t, text, s.Text, fmt.Sprintf("Text should be %s, got %s", text, s.Text)) +} + +func TestV3NewSubscriptionTrackingSetHtml(t *testing.T) { + html := "

hello

" + s := NewSubscriptionTrackingSetting().SetHTML(html) + + assert.Equal(t, html, s.Html, fmt.Sprintf("Html should be %s, got %s", html, s.Html)) +} + +func TestV3NewGaSetting(t *testing.T) { + assert.NotNil(t, NewGaSetting(), "NewGaSetting() shouldn't return nil") +} + +func TestV3GaSettingSetCampaignName(t *testing.T) { + campaignName := "campaign1" + g := NewGaSetting().SetCampaignName(campaignName) + + assert.Equal(t, campaignName, g.CampaignName, fmt.Sprintf("CampaignName should be %s, got %s", campaignName, g.CampaignName)) +} + +func TestV3GaSettingSetCampaignTerm(t *testing.T) { + campaignTerm := "campaign1_term" + g := NewGaSetting().SetCampaignTerm(campaignTerm) + + assert.Equal(t, campaignTerm, g.CampaignTerm, fmt.Sprintf("CampaignTerm should be %s, got %s", campaignTerm, g.CampaignTerm)) +} + +func TestV3GaSettingSetCampaignSource(t *testing.T) { + campaignSource := "campaign1_source" + g := NewGaSetting().SetCampaignSource(campaignSource) + + assert.Equal(t, campaignSource, g.CampaignSource, fmt.Sprintf("CampaignSource should be %s, got %s", campaignSource, g.CampaignSource)) +} + +func TestV3GaSettingSetCampaignContent(t *testing.T) { + campaignContent := "campaign1_content" + g := NewGaSetting().SetCampaignContent(campaignContent) + + assert.Equal(t, campaignContent, g.CampaignContent, fmt.Sprintf("CampaignContent should be %s, got %s", campaignContent, g.CampaignContent)) +} + +func TestV3NewSetting(t *testing.T) { + s := NewSetting(true) + + assert.NotNil(t, s, "NewSetting() shouldn't return nil") + assert.True(t, *s.Enable, "NewSetting(true) should return a setting with Enabled = true") +} + +func TestV3NewEmail(t *testing.T) { + name := "Johnny" + address := "Johnny@rocket.io" + + e := NewEmail(name, address) + + assert.Equal(t, name, e.Name, fmt.Sprintf("Name should be %s, got %s", name, e.Name)) + assert.Equal(t, address, e.Address, fmt.Sprintf("Address should be %s, got %s", address, e.Address)) +} + +func TestV3NewSingleEmail(t *testing.T) { + from := NewEmail("Example User", "test@example.com") + subject := "Sending with Twilio SendGrid is Fun" + to := NewEmail("Example User", "test@example.com") + plainTextContent := "and easy to do anywhere, even with Go" + htmlContent := "and easy to do anywhere, even with Go" + + message := NewSingleEmail(from, subject, to, plainTextContent, htmlContent) + + assert.NotNil(t, message, "NewV3MailInit() shouldn't return nil") + assert.NotNil(t, message.From, "From shouldn't return nil") + assert.Equal(t, subject, message.Subject, fmt.Sprintf("Subject should be %s, got %s", subject, message.Subject)) + assert.NotNil(t, message.Content, "Content shouldn't be nil") +} + +func TestV3NewSingleEmailWithEmptyHTMLContent(t *testing.T) { + from := NewEmail("Example User", "test@example.com") + subject := "Sending with Twilio SendGrid is Fun" + to := NewEmail("Example User", "test@example.com") + plainTextContent := "and easy to do anywhere, even with Go" + + message := NewSingleEmail(from, subject, to, plainTextContent, "") + + m, _ := json.Marshal(message) + fmt.Println(string(m)) + + if message == nil { + t.Errorf("NewV3MailInit() shouldn't return nil") + } + + if message.From == nil { + t.Errorf("From shouldn't be nil") + } + + if message.Subject != subject { + t.Errorf("Subject should be %s, got %s", subject, message.Subject) + } + + if message.Content == nil { + t.Errorf("Content shouldn't be nil") + } + + if len(message.Content) != 1 { + t.Errorf("Content length should be 1, got %d", len(message.Content)) + } + + if len(message.Content) == 1 && message.Content[0].Type != "text/plain" { + t.Errorf("Content type should be 'text/plain', got %s", message.Content[0].Type) + } +} + +func TestV3NewSingleEmailPlainText(t *testing.T) { + from := NewEmail("Example User", "test@example.com") + subject := "Sending with SendGrid is Fun" + to := NewEmail("Example User", "test@example.com") + plainTextContent := "and easy to do anywhere, even with Go" + + message := NewSingleEmailPlainText(from, subject, to, plainTextContent) + + m, _ := json.Marshal(message) + fmt.Println(string(m)) + + assert.NotNil(t, message, "NewV3MailInit() shouldn't return nil") + assert.NotNil(t, message.From, "From shouldn't return nil") + assert.Equal(t, subject, message.Subject, fmt.Sprintf("Subject should be %s, got %s", subject, message.Subject)) + assert.NotNil(t, message.Content, "Content shouldn't be nil") +} + +func TestV3NewClickTrackingSetting(t *testing.T) { + c := NewClickTrackingSetting() + c.SetEnable(true) + c.SetEnableText(false) + + assert.True(t, *c.Enable, "Click Tracking should be enabled") + assert.False(t, *c.EnableText, "Enable Text should not be enabled") +} + +func TestV3NewSpamCheckSetting(t *testing.T) { + spamThreshold := 8 + postToURL := "http://myurl.com" + s := NewSpamCheckSetting() + s.SetEnable(true) + s.SetSpamThreshold(spamThreshold) + s.SetPostToURL(postToURL) + + assert.True(t, *s.Enable, "SpamCheck should be enabled") + assert.Equal(t, spamThreshold, s.SpamThreshold, fmt.Sprintf("SpamThreshold should be %d, got %d", spamThreshold, s.SpamThreshold)) + assert.Equal(t, postToURL, s.PostToURL, fmt.Sprintf("PostToURL should be %s, got %s", postToURL, s.PostToURL)) +} + +func TestV3NewSandboxModeSetting(t *testing.T) { + spamCheck := NewSpamCheckSetting() + spamCheck.SetEnable(true) + spamCheck.SetSpamThreshold(1) + spamCheck.SetPostToURL("http://wwww.google.com") + s := NewSandboxModeSetting(true, true, spamCheck) + + assert.True(t, *s.Enable, "Sandbox Mode should be enabled") + assert.True(t, *s.ForwardSpam, "ForwardSpam should be enabled") + assert.NotNil(t, s.SpamCheck, "SpamCheck should not be nil") +} + +func TestParseEmail(t *testing.T) { + e, err := ParseEmail("example example ") + if err != nil { + t.Error("Email should have been parsed successfully") + } + expectedName := "example example" + if e.Name != expectedName { + t.Errorf("Expect email with name %s but got %s", expectedName, e.Name) + } + expectedAddress := "example@example.com" + if e.Address != expectedAddress { + t.Errorf("Expect email with address %s but got %s", expectedAddress, e.Address) + } +} + +func TestParseInvalidEmail(t *testing.T) { + _, err := ParseEmail("example example ") + if err == nil { + t.Error("Expected an error to be thrown from ParseEmail") + } +} + +func TestParseInvalidEmailLength(t *testing.T) { + _, err := ParseEmail("example example ") + if err != nil { + t.Error("ParseEmail should have been parsed successfully") + } + + _, err = ParseEmail("example example ") + if err == nil { + t.Error("Expected an error to be thrown from ParseEmail") + } + + _, err = ParseEmail("example example ") + if err == nil { + t.Error("Expected an error to be thrown from ParseEmail") + } + + _, err = ParseEmail("example example ") + if err == nil { + t.Error("Expected an error to be thrown from ParseEmail") + } +} diff --git a/v3/sendgrid.go b/v3/sendgrid.go new file mode 100644 index 00000000..7192bec2 --- /dev/null +++ b/v3/sendgrid.go @@ -0,0 +1,49 @@ +package sendgrid + +import ( + "github.com/sendgrid/rest" +) + +// sendGridOptions for CreateRequest +type sendGridOptions struct { + Key string + Endpoint string + Host string + Subuser string +} + +// GetRequest +// @return [Request] a default request object +func GetRequest(key, endpoint, host string) rest.Request { + return createSendGridRequest(sendGridOptions{key, endpoint, host, ""}) +} + +// GetRequestSubuser like GetRequest but with On-Behalf of Subuser +// @return [Request] a default request object +func GetRequestSubuser(key, endpoint, host, subuser string) rest.Request { + return createSendGridRequest(sendGridOptions{key, endpoint, host, subuser}) +} + +// createSendGridRequest create Request +// @return [Request] a default request object +func createSendGridRequest(sgOptions sendGridOptions) rest.Request { + options := options{ + "Bearer " + sgOptions.Key, + sgOptions.Endpoint, + sgOptions.Host, + sgOptions.Subuser, + } + + if options.Host == "" { + options.Host = "https://api.sendgrid.com" + } + + return requestNew(options) +} + +// NewSendClient constructs a new Twilio SendGrid client given an API key +func NewSendClient(key string) *Client { + request := GetRequest(key, "/v3/mail/send", "") + request.Method = "POST" + return &Client{request} +} diff --git a/v3/sendgrid_test.go b/v3/sendgrid_test.go new file mode 100644 index 00000000..68d5e22a --- /dev/null +++ b/v3/sendgrid_test.go @@ -0,0 +1,3733 @@ +package sendgrid + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strconv" + "strings" + "testing" + "time" + + "github.com/sendgrid/rest" + "github.com/sendgrid/sendgrid-go/helpers/mail" + "github.com/stretchr/testify/assert" +) + +func TestGetRequest(t *testing.T) { + request := GetRequest("", "", "") + assert.Equal(t, "https://api.sendgrid.com", request.BaseURL, "Host default not set") + assert.Equal(t, "Bearer ", request.Headers["Authorization"], "Wrong default Authorization") + assert.Equal(t, "sendgrid/"+Version+";go", request.Headers["User-Agent"], "Wrong default User Agent") + request = GetRequest("API_KEY", "/v3/endpoint", "https://test.api.com") + assert.Equal(t, "Bearer API_KEY", request.Headers["Authorization"], "Wrong Authorization") + assert.Equal(t, "sendgrid/"+Version+";go", request.Headers["User-Agent"], "Wrong User Agent") + assert.Equal(t, "application/json", request.Headers["Accept"], "Wrong Accept Agent") +} + +func ShouldHaveHeaders(request *rest.Request, t *testing.T) { + if request.Headers["Authorization"] != "Bearer API_KEY" { + t.Error("Wrong Authorization") + } + if request.Headers["User-Agent"] != "sendgrid/"+Version+";go" { + t.Error("Wrong User Agent") + } + if request.Headers["Accept"] != "application/json" { + t.Error("Wrong Accept header") + } + if request.Headers["On-Behalf-Of"] != "subuserUsername" { + t.Error("Wrong On-Behalf-Of") + } +} + +func TestGetRequestSubuser(t *testing.T) { + request := GetRequestSubuser("API_KEY", "/v3/endpoint", "https://test.api.com", "subuserUsername") + + if request.BaseURL != "https://test.api.com/v3/endpoint" { + t.Error("Host not set correctly") + } + + ShouldHaveHeaders(&request, t) +} + +func getRequest(endpoint string) rest.Request { + return GetRequest("SENDGRID_APIKEY", endpoint, "") +} + +func TestCustomHTTPClient(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(time.Millisecond * 20) + fmt.Fprintln(w, "{\"message\": \"success\"}") + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + _, err := custom.Send(request) + assert.NotNil(t, err, "A timeout did not trigger as expected") + assert.True(t, strings.Contains(err.Error(), "Client.Timeout exceeded while awaiting headers"), "We did not receive the Timeout error") +} + +func TestRequestRetry_rateLimit(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-RateLimit-Reset", strconv.Itoa(int(time.Now().Add(1*time.Second).Unix()))) + w.WriteHeader(http.StatusTooManyRequests) + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + DefaultClient = &custom + _, err := MakeRequestRetry(request) + assert.NotNil(t, err, "An error did not trigger") + assert.True(t, strings.Contains(err.Error(), "rate limit retry exceeded"), "We did not receive the rate limit error") + DefaultClient = rest.DefaultClient +} + +func TestRequestRetry_rateLimit_noHeader(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTooManyRequests) + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + DefaultClient = &custom + _, err := MakeRequestRetry(request) + assert.NotNil(t, err, "An error did not trigger") + assert.True(t, strings.Contains(err.Error(), "rate limit retry exceeded"), "We did not receive the rate limit error") + DefaultClient = rest.DefaultClient +} + +func TestRequestRetryWithContext_cancel(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTooManyRequests) + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + DefaultClient = &custom + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + time.Sleep(500 * time.Millisecond) + cancel() + }() + _, err := MakeRequestRetryWithContext(ctx, request) + + assert.NotNil(t, err, "An error did not trigger") + assert.Equal(t, context.Canceled, err) + DefaultClient = rest.DefaultClient +} + +func TestRequestAsync(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + DefaultClient = &custom + r, e := MakeRequestAsync(request) + + select { + case <-r: + case err := <-e: + t.Errorf("Received an error,:%v", err) + case <-time.After(10 * time.Second): + t.Error("Timed out waiting for a response") + } + DefaultClient = rest.DefaultClient +} + +func TestRequestAsync_rateLimit(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-RateLimit-Reset", strconv.Itoa(int(time.Now().Add(1*time.Second).Unix()))) + w.WriteHeader(http.StatusTooManyRequests) + })) + defer fakeServer.Close() + apiKey := "SENDGRID_APIKEY" + host := fakeServer.URL + request := GetRequest(apiKey, "/v3/test_endpoint", host) + request.Method = "GET" + var custom rest.Client + custom.HTTPClient = &http.Client{Timeout: time.Millisecond * 10} + DefaultClient = &custom + r, e := MakeRequestAsync(request) + + select { + case <-r: + t.Error("Received a valid response") + return + case err := <-e: + assert.True(t, strings.Contains(err.Error(), "rate limit retry exceeded"), "We did not receive the rate limit error") + case <-time.After(10 * time.Second): + t.Error("Timed out waiting for an error") + } + DefaultClient = rest.DefaultClient +} + +func Test_test_access_settings_activity_get(t *testing.T) { + request := getRequest("/v3/access_settings/activity") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_access_settings_whitelist_post(t *testing.T) { + request := getRequest("/v3/access_settings/whitelist") + request.Method = "POST" + request.Body = []byte(` { + "ips": [ + { + "ip": "192.168.1.1" + }, + { + "ip": "192.*.*.*" + }, + { + "ip": "192.168.1.3/32" + } + ] +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_access_settings_whitelist_get(t *testing.T) { + request := getRequest("/v3/access_settings/whitelist") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_access_settings_whitelist_delete(t *testing.T) { + request := getRequest("/v3/access_settings/whitelist") + request.Method = "DELETE" + request.Body = []byte(` { + "ids": [ + 1, + 2, + 3 + ] +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_access_settings_whitelist__rule_id__get(t *testing.T) { + request := getRequest("/v3/access_settings/whitelist/{rule_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_access_settings_whitelist__rule_id__delete(t *testing.T) { + request := getRequest("/v3/access_settings/whitelist/{rule_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_alerts_post(t *testing.T) { + request := getRequest("/v3/alerts") + request.Method = "POST" + request.Body = []byte(` { + "email_to": "example@example.com", + "frequency": "daily", + "type": "stats_notification" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_alerts_get(t *testing.T) { + request := getRequest("/v3/alerts") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_alerts__alert_id__patch(t *testing.T) { + request := getRequest("/v3/alerts/{alert_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "email_to": "example@example.com" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_alerts__alert_id__get(t *testing.T) { + request := getRequest("/v3/alerts/{alert_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_alerts__alert_id__delete(t *testing.T) { + request := getRequest("/v3/alerts/{alert_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys_post(t *testing.T) { + request := getRequest("/v3/api_keys") + request.Method = "POST" + request.Body = []byte(` { + "name": "My API Key", + "sample": "data", + "scopes": [ + "mail.send", + "alerts.create", + "alerts.read" + ] +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys_get(t *testing.T) { + request := getRequest("/v3/api_keys") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys__api_key_id__put(t *testing.T) { + request := getRequest("/v3/api_keys/{api_key_id}") + request.Method = "PUT" + request.Body = []byte(` { + "name": "A New Hope", + "scopes": [ + "user.profile.read", + "user.profile.update" + ] +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys__api_key_id__patch(t *testing.T) { + request := getRequest("/v3/api_keys/{api_key_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "name": "A New Hope" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys__api_key_id__get(t *testing.T) { + request := getRequest("/v3/api_keys/{api_key_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_api_keys__api_key_id__delete(t *testing.T) { + request := getRequest("/v3/api_keys/{api_key_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups_post(t *testing.T) { + request := getRequest("/v3/asm/groups") + request.Method = "POST" + request.Body = []byte(` { + "description": "Suggestions for products our users might like.", + "is_default": true, + "name": "Product Suggestions" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups_get(t *testing.T) { + request := getRequest("/v3/asm/groups") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__patch(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "description": "Suggestions for items our users might like.", + "id": 103, + "name": "Item Suggestions" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__get(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__delete(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__suppressions_post(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}/suppressions") + request.Method = "POST" + request.Body = []byte(` { + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__suppressions_get(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}/suppressions") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__suppressions_search_post(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}/suppressions/search") + request.Method = "POST" + request.Body = []byte(` { + "recipient_emails": [ + "exists1@example.com", + "exists2@example.com", + "doesnotexists@example.com" + ] +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_groups__group_id__suppressions__email__delete(t *testing.T) { + request := getRequest("/v3/asm/groups/{group_id}/suppressions/{email}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_suppressions_get(t *testing.T) { + request := getRequest("/v3/asm/suppressions") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_suppressions_global_post(t *testing.T) { + request := getRequest("/v3/asm/suppressions/global") + request.Method = "POST" + request.Body = []byte(` { + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_suppressions_global__email__get(t *testing.T) { + request := getRequest("/v3/asm/suppressions/global/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_suppressions_global__email__delete(t *testing.T) { + request := getRequest("/v3/asm/suppressions/global/{email}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_asm_suppressions__email__get(t *testing.T) { + request := getRequest("/v3/asm/suppressions/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_browsers_stats_get(t *testing.T) { + request := getRequest("/v3/browsers/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["aggregated_by"] = "day" + queryParams["browsers"] = "test_string" + queryParams["limit"] = "test_string" + queryParams["offset"] = "test_string" + queryParams["start_date"] = "2016-01-01" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns_post(t *testing.T) { + request := getRequest("/v3/campaigns") + request.Method = "POST" + request.Body = []byte(` { + "categories": [ + "spring line" + ], + "custom_unsubscribe_url": "", + "html_content": "

Check out our spring line!

", + "ip_pool": "marketing", + "list_ids": [ + 110, + 124 + ], + "plain_content": "Check out our spring line!", + "segment_ids": [ + 110 + ], + "sender_id": 124451, + "subject": "New Products for Spring!", + "suppression_group_id": 42, + "title": "March Newsletter" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns_get(t *testing.T) { + request := getRequest("/v3/campaigns") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__patch(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "categories": [ + "summer line" + ], + "html_content": "

Check out our summer line!

", + "plain_content": "Check out our summer line!", + "subject": "New Products for Summer!", + "title": "May Newsletter" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__get(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__delete(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_patch(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules") + request.Method = "PATCH" + request.Body = []byte(` { + "send_at": 1489451436 +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_post(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules") + request.Method = "POST" + request.Body = []byte(` { + "send_at": 1489771528 +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_get(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_delete(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_now_post(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules/now") + request.Method = "POST" + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_campaigns__campaign_id__schedules_test_post(t *testing.T) { + request := getRequest("/v3/campaigns/{campaign_id}/schedules/test") + request.Method = "POST" + request.Body = []byte(` { + "to": "your.email@example.com" +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_categories_get(t *testing.T) { + request := getRequest("/v3/categories") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["category"] = "test_string" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_categories_stats_get(t *testing.T) { + request := getRequest("/v3/categories/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["categories"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_categories_stats_sums_get(t *testing.T) { + request := getRequest("/v3/categories/stats/sums") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["sort_by_metric"] = "test_string" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["sort_by_direction"] = "asc" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_clients_stats_get(t *testing.T) { + request := getRequest("/v3/clients/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["aggregated_by"] = "day" + queryParams["start_date"] = "2016-01-01" + queryParams["end_date"] = "2016-04-01" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_clients__client_type__stats_get(t *testing.T) { + request := getRequest("/v3/clients/{client_type}/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["aggregated_by"] = "day" + queryParams["start_date"] = "2016-01-01" + queryParams["end_date"] = "2016-04-01" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_custom_fields_post(t *testing.T) { + request := getRequest("/v3/contactdb/custom_fields") + request.Method = "POST" + request.Body = []byte(` { + "name": "pet", + "type": "text" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_custom_fields_get(t *testing.T) { + request := getRequest("/v3/contactdb/custom_fields") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_custom_fields__custom_field_id__get(t *testing.T) { + request := getRequest("/v3/contactdb/custom_fields/{custom_field_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_custom_fields__custom_field_id__delete(t *testing.T) { + request := getRequest("/v3/contactdb/custom_fields/{custom_field_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "202" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 202, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists_post(t *testing.T) { + request := getRequest("/v3/contactdb/lists") + request.Method = "POST" + request.Body = []byte(` { + "name": "your list name" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists_get(t *testing.T) { + request := getRequest("/v3/contactdb/lists") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists_delete(t *testing.T) { + request := getRequest("/v3/contactdb/lists") + request.Method = "DELETE" + request.Body = []byte(` [ + 1, + 2, + 3, + 4 +]`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__patch(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "name": "newlistname" +}`) + queryParams := make(map[string]string) + queryParams["list_id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__get(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["list_id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__delete(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}") + request.Method = "DELETE" + queryParams := make(map[string]string) + queryParams["delete_contacts"] = "true" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "202" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 202, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__recipients_post(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}/recipients") + request.Method = "POST" + request.Body = []byte(` [ + "recipient_id1", + "recipient_id2" +]`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__recipients_get(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}/recipients") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["page"] = "1" + queryParams["page_size"] = "1" + queryParams["list_id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__recipients__recipient_id__post(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}/recipients/{recipient_id}") + request.Method = "POST" + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_lists__list_id__recipients__recipient_id__delete(t *testing.T) { + request := getRequest("/v3/contactdb/lists/{list_id}/recipients/{recipient_id}") + request.Method = "DELETE" + queryParams := make(map[string]string) + queryParams["recipient_id"] = "1" + queryParams["list_id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_patch(t *testing.T) { + request := getRequest("/v3/contactdb/recipients") + request.Method = "PATCH" + request.Body = []byte(` [ + { + "email": "jones@example.com", + "first_name": "Guy", + "last_name": "Jones" + } +]`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_post(t *testing.T) { + request := getRequest("/v3/contactdb/recipients") + request.Method = "POST" + request.Body = []byte(` [ + { + "age": 25, + "email": "example@example.com", + "first_name": "", + "last_name": "User" + }, + { + "age": 25, + "email": "example2@example.com", + "first_name": "Example", + "last_name": "User" + } +]`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["page"] = "1" + queryParams["page_size"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_delete(t *testing.T) { + request := getRequest("/v3/contactdb/recipients") + request.Method = "DELETE" + request.Body = []byte(` [ + "recipient_id1", + "recipient_id2" +]`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_billable_count_get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/billable_count") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_count_get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/count") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients_search_get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/search") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["{field_name}"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients__recipient_id__get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/{recipient_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients__recipient_id__delete(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/{recipient_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_recipients__recipient_id__lists_get(t *testing.T) { + request := getRequest("/v3/contactdb/recipients/{recipient_id}/lists") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_reserved_fields_get(t *testing.T) { + request := getRequest("/v3/contactdb/reserved_fields") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments_post(t *testing.T) { + request := getRequest("/v3/contactdb/segments") + request.Method = "POST" + request.Body = []byte(` { + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + }, + { + "and_or": "and", + "field": "last_clicked", + "operator": "gt", + "value": "01/02/2015" + }, + { + "and_or": "or", + "field": "clicks.campaign_identifier", + "operator": "eq", + "value": "513" + } + ], + "list_id": 4, + "name": "Last Name Miller" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments_get(t *testing.T) { + request := getRequest("/v3/contactdb/segments") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments__segment_id__patch(t *testing.T) { + request := getRequest("/v3/contactdb/segments/{segment_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + } + ], + "list_id": 5, + "name": "The Millers" +}`) + queryParams := make(map[string]string) + queryParams["segment_id"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments__segment_id__get(t *testing.T) { + request := getRequest("/v3/contactdb/segments/{segment_id}") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["segment_id"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments__segment_id__delete(t *testing.T) { + request := getRequest("/v3/contactdb/segments/{segment_id}") + request.Method = "DELETE" + queryParams := make(map[string]string) + queryParams["delete_contacts"] = "true" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_contactdb_segments__segment_id__recipients_get(t *testing.T) { + request := getRequest("/v3/contactdb/segments/{segment_id}/recipients") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["page"] = "1" + queryParams["page_size"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_devices_stats_get(t *testing.T) { + request := getRequest("/v3/devices/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["end_date"] = "2016-04-01" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_geo_stats_get(t *testing.T) { + request := getRequest("/v3/geo/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["country"] = "US" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_get(t *testing.T) { + request := getRequest("/v3/ips") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["subuser"] = "test_string" + queryParams["ip"] = "test_string" + queryParams["limit"] = "1" + queryParams["exclude_whitelabels"] = "true" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_assigned_get(t *testing.T) { + request := getRequest("/v3/ips/assigned") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools_post(t *testing.T) { + request := getRequest("/v3/ips/pools") + request.Method = "POST" + request.Body = []byte(` { + "name": "marketing" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools_get(t *testing.T) { + request := getRequest("/v3/ips/pools") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools__pool_name__put(t *testing.T) { + request := getRequest("/v3/ips/pools/{pool_name}") + request.Method = "PUT" + request.Body = []byte(` { + "name": "new_pool_name" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools__pool_name__get(t *testing.T) { + request := getRequest("/v3/ips/pools/{pool_name}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools__pool_name__delete(t *testing.T) { + request := getRequest("/v3/ips/pools/{pool_name}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools__pool_name__ips_post(t *testing.T) { + request := getRequest("/v3/ips/pools/{pool_name}/ips") + request.Method = "POST" + request.Body = []byte(` { + "ip": "0.0.0.0" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_pools__pool_name__ips__ip__delete(t *testing.T) { + request := getRequest("/v3/ips/pools/{pool_name}/ips/{ip}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_warmup_post(t *testing.T) { + request := getRequest("/v3/ips/warmup") + request.Method = "POST" + request.Body = []byte(` { + "ip": "0.0.0.0" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_warmup_get(t *testing.T) { + request := getRequest("/v3/ips/warmup") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_warmup__ip_address__get(t *testing.T) { + request := getRequest("/v3/ips/warmup/{ip_address}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips_warmup__ip_address__delete(t *testing.T) { + request := getRequest("/v3/ips/warmup/{ip_address}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_ips__ip_address__get(t *testing.T) { + request := getRequest("/v3/ips/{ip_address}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_batch_post(t *testing.T) { + request := getRequest("/v3/mail/batch") + request.Method = "POST" + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_batch__batch_id__get(t *testing.T) { + request := getRequest("/v3/mail/batch/{batch_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_send_client(t *testing.T) { + apiKey := "SENDGRID_APIKEY" + client := NewSendClient(apiKey) + + emailBytes := []byte(` { + "asm": { + "group_id": 1, + "groups_to_display": [ + 1, + 2, + 3 + ] + }, + "attachments": [ + { + "content": "[BASE64 encoded content block here]", + "content_id": "ii_139db99fdb5c3704", + "disposition": "inline", + "filename": "file1.jpg", + "name": "file1", + "type": "jpg" + } + ], + "batch_id": "[YOUR BATCH ID GOES HERE]", + "categories": [ + "category1", + "category2" + ], + "content": [ + { + "type": "text/html", + "value": "

Hello, world!

" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "from": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "headers": {}, + "ip_pool_name": "[YOUR POOL NAME GOES HERE]", + "mail_settings": { + "bcc": { + "email": "ben.doe@example.com", + "enable": true + }, + "bypass_list_management": { + "enable": true + }, + "footer": { + "enable": true, + "html": "

Thanks
The Twilio SendGrid Team

", + "text": "Thanks,/n The Twilio SendGrid Team" + }, + "sandbox_mode": { + "enable": false + }, + "spam_check": { + "enable": true, + "post_to_url": "http://example.com/compliance", + "threshold": 3 + } + }, + "personalizations": [ + { + "bcc": [ + { + "email": "sam.doe@example.com", + "name": "Sam Doe" + } + ], + "cc": [ + { + "email": "jane.doe@example.com", + "name": "Jane Doe" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "headers": { + "X-Accept-Language": "en", + "X-Mailer": "MyApp" + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "substitutions": { + "id": "substitutions", + "type": "object" + }, + "to": [ + { + "email": "john.doe@example.com", + "name": "John Doe" + } + ] + } + ], + "reply_to": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "template_id": "[YOUR TEMPLATE ID GOES HERE]", + "tracking_settings": { + "click_tracking": { + "enable": true, + "enable_text": true + }, + "ganalytics": { + "enable": true, + "utm_campaign": "[NAME OF YOUR REFERRER SOURCE]", + "utm_content": "[USE THIS SPACE TO DIFFERENTIATE YOUR EMAIL FROM ADS]", + "utm_medium": "[NAME OF YOUR MARKETING MEDIUM e.g. email]", + "utm_name": "[NAME OF YOUR CAMPAIGN]", + "utm_term": "[IDENTIFY PAID KEYWORDS HERE]" + }, + "open_tracking": { + "enable": true, + "substitution_tag": "%opentrack" + }, + "subscription_tracking": { + "enable": true, + "html": "If you would like to unsubscribe and stop receiving these emails <% clickhere %>.", + "substitution_tag": "<%click here%>", + "text": "If you would like to unsubscribe and stop receiving these emails <% click here %>." + } + } + }`) + email := &mail.SGMailV3{} + err := json.Unmarshal(emailBytes, email) + assert.Nil(t, err, fmt.Sprintf("Unmarshal error: %v", err)) + client.Request.Headers["X-Mock"] = "202" + response, err := client.Send(email) + if err != nil { + t.Log(err) + } + assert.Equal(t, 202, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_send_post(t *testing.T) { + request := getRequest("/v3/mail/send") + request.Method = "POST" + request.Body = []byte(` { + "asm": { + "group_id": 1, + "groups_to_display": [ + 1, + 2, + 3 + ] + }, + "attachments": [ + { + "content": "[BASE64 encoded content block here]", + "content_id": "ii_139db99fdb5c3704", + "disposition": "inline", + "filename": "file1.jpg", + "name": "file1", + "type": "jpg" + } + ], + "batch_id": "[YOUR BATCH ID GOES HERE]", + "categories": [ + "category1", + "category2" + ], + "content": [ + { + "type": "text/html", + "value": "

Hello, world!

" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "from": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "headers": {}, + "ip_pool_name": "[YOUR POOL NAME GOES HERE]", + "mail_settings": { + "bcc": { + "email": "ben.doe@example.com", + "enable": true + }, + "bypass_list_management": { + "enable": true + }, + "footer": { + "enable": true, + "html": "

Thanks
The Twilio SendGrid Team

", + "text": "Thanks,/n The Twilio SendGrid Team" + }, + "sandbox_mode": { + "enable": false + }, + "spam_check": { + "enable": true, + "post_to_url": "http://example.com/compliance", + "threshold": 3 + } + }, + "personalizations": [ + { + "bcc": [ + { + "email": "sam.doe@example.com", + "name": "Sam Doe" + } + ], + "cc": [ + { + "email": "jane.doe@example.com", + "name": "Jane Doe" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "headers": { + "X-Accept-Language": "en", + "X-Mailer": "MyApp" + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "substitutions": { + "id": "substitutions", + "type": "object" + }, + "to": [ + { + "email": "john.doe@example.com", + "name": "John Doe" + } + ] + } + ], + "reply_to": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "sections": { + "section": { + ":sectionName1": "section 1 text", + ":sectionName2": "section 2 text" + } + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "template_id": "[YOUR TEMPLATE ID GOES HERE]", + "tracking_settings": { + "click_tracking": { + "enable": true, + "enable_text": true + }, + "ganalytics": { + "enable": true, + "utm_campaign": "[NAME OF YOUR REFERRER SOURCE]", + "utm_content": "[USE THIS SPACE TO DIFFERENTIATE YOUR EMAIL FROM ADS]", + "utm_medium": "[NAME OF YOUR MARKETING MEDIUM e.g. email]", + "utm_name": "[NAME OF YOUR CAMPAIGN]", + "utm_term": "[IDENTIFY PAID KEYWORDS HERE]" + }, + "open_tracking": { + "enable": true, + "substitution_tag": "%opentrack" + }, + "subscription_tracking": { + "enable": true, + "html": "If you would like to unsubscribe and stop receiving these emails <% clickhere %>.", + "substitution_tag": "<%click here%>", + "text": "If you would like to unsubscribe and stop receiving these emails <% click here %>." + } + } +}`) + request.Headers["X-Mock"] = "202" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 202, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_get(t *testing.T) { + request := getRequest("/v3/mail_settings") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_address_whitelist_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/address_whitelist") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "list": [ + "email1@example.com", + "example.com" + ] +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_address_whitelist_get(t *testing.T) { + request := getRequest("/v3/mail_settings/address_whitelist") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_bcc_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/bcc") + request.Method = "PATCH" + request.Body = []byte(` { + "email": "email@example.com", + "enabled": false +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_bcc_get(t *testing.T) { + request := getRequest("/v3/mail_settings/bcc") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_bounce_purge_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/bounce_purge") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "hard_bounces": 5, + "soft_bounces": 5 +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_bounce_purge_get(t *testing.T) { + request := getRequest("/v3/mail_settings/bounce_purge") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_footer_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/footer") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "html_content": "...", + "plain_content": "..." +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_footer_get(t *testing.T) { + request := getRequest("/v3/mail_settings/footer") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_forward_bounce_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/forward_bounce") + request.Method = "PATCH" + request.Body = []byte(` { + "email": "example@example.com", + "enabled": true +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_forward_bounce_get(t *testing.T) { + request := getRequest("/v3/mail_settings/forward_bounce") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_forward_spam_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/forward_spam") + request.Method = "PATCH" + request.Body = []byte(` { + "email": "", + "enabled": false +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_forward_spam_get(t *testing.T) { + request := getRequest("/v3/mail_settings/forward_spam") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_plain_content_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/plain_content") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": false +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_plain_content_get(t *testing.T) { + request := getRequest("/v3/mail_settings/plain_content") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_spam_check_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/spam_check") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "max_score": 5, + "url": "url" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_spam_check_get(t *testing.T) { + request := getRequest("/v3/mail_settings/spam_check") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_template_patch(t *testing.T) { + request := getRequest("/v3/mail_settings/template") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "html_content": "<% body %>" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mail_settings_template_get(t *testing.T) { + request := getRequest("/v3/mail_settings/template") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_mailbox_providers_stats_get(t *testing.T) { + request := getRequest("/v3/mailbox_providers/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["mailbox_providers"] = "test_string" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_partner_settings_get(t *testing.T) { + request := getRequest("/v3/partner_settings") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_partner_settings_new_relic_patch(t *testing.T) { + request := getRequest("/v3/partner_settings/new_relic") + request.Method = "PATCH" + request.Body = []byte(` { + "enable_subuser_statistics": true, + "enabled": true, + "license_key": "" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_partner_settings_new_relic_get(t *testing.T) { + request := getRequest("/v3/partner_settings/new_relic") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_scopes_get(t *testing.T) { + request := getRequest("/v3/scopes") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders_post(t *testing.T) { + request := getRequest("/v3/senders") + request.Method = "POST" + request.Body = []byte(` { + "address": "123 Elm St.", + "address_2": "Apt. 456", + "city": "Denver", + "country": "United States", + "from": { + "email": "from@example.com", + "name": "Example INC" + }, + "nickname": "My Sender ID", + "reply_to": { + "email": "replyto@example.com", + "name": "Example INC" + }, + "state": "Colorado", + "zip": "80202" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders_get(t *testing.T) { + request := getRequest("/v3/senders") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders__sender_id__patch(t *testing.T) { + request := getRequest("/v3/senders/{sender_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "address": "123 Elm St.", + "address_2": "Apt. 456", + "city": "Denver", + "country": "United States", + "from": { + "email": "from@example.com", + "name": "Example INC" + }, + "nickname": "My Sender ID", + "reply_to": { + "email": "replyto@example.com", + "name": "Example INC" + }, + "state": "Colorado", + "zip": "80202" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders__sender_id__get(t *testing.T) { + request := getRequest("/v3/senders/{sender_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders__sender_id__delete(t *testing.T) { + request := getRequest("/v3/senders/{sender_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_senders__sender_id__resend_verification_post(t *testing.T) { + request := getRequest("/v3/senders/{sender_id}/resend_verification") + request.Method = "POST" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_stats_get(t *testing.T) { + request := getRequest("/v3/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["end_date"] = "2016-04-01" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_post(t *testing.T) { + request := getRequest("/v3/subusers") + request.Method = "POST" + request.Body = []byte(` { + "email": "John@example.com", + "ips": [ + "1.1.1.1", + "2.2.2.2" + ], + "password": "johns_password", + "username": "John@example.com" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_get(t *testing.T) { + request := getRequest("/v3/subusers") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["username"] = "test_string" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_reputations_get(t *testing.T) { + request := getRequest("/v3/subusers/reputations") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["usernames"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_stats_get(t *testing.T) { + request := getRequest("/v3/subusers/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["subusers"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_stats_monthly_get(t *testing.T) { + request := getRequest("/v3/subusers/stats/monthly") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["subuser"] = "test_string" + queryParams["limit"] = "1" + queryParams["sort_by_metric"] = "test_string" + queryParams["offset"] = "1" + queryParams["date"] = "test_string" + queryParams["sort_by_direction"] = "asc" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers_stats_sums_get(t *testing.T) { + request := getRequest("/v3/subusers/stats/sums") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["end_date"] = "2016-04-01" + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "1" + queryParams["sort_by_metric"] = "test_string" + queryParams["offset"] = "1" + queryParams["start_date"] = "2016-01-01" + queryParams["sort_by_direction"] = "asc" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__patch(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}") + request.Method = "PATCH" + request.Body = []byte(` { + "disabled": false +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__delete(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__ips_put(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/ips") + request.Method = "PUT" + request.Body = []byte(` [ + "127.0.0.1" +]`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__monitor_put(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/monitor") + request.Method = "PUT" + request.Body = []byte(` { + "email": "example@example.com", + "frequency": 500 +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__monitor_post(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/monitor") + request.Method = "POST" + request.Body = []byte(` { + "email": "example@example.com", + "frequency": 50000 +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__monitor_get(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/monitor") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__monitor_delete(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/monitor") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_subusers__subuser_name__stats_monthly_get(t *testing.T) { + request := getRequest("/v3/subusers/{subuser_name}/stats/monthly") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["date"] = "test_string" + queryParams["sort_by_direction"] = "asc" + queryParams["limit"] = "1" + queryParams["sort_by_metric"] = "test_string" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_blocks_get(t *testing.T) { + request := getRequest("/v3/suppression/blocks") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["start_time"] = "1" + queryParams["limit"] = "1" + queryParams["end_time"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_blocks_delete(t *testing.T) { + request := getRequest("/v3/suppression/blocks") + request.Method = "DELETE" + request.Body = []byte(` { + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_blocks__email__get(t *testing.T) { + request := getRequest("/v3/suppression/blocks/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_blocks__email__delete(t *testing.T) { + request := getRequest("/v3/suppression/blocks/{email}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_bounces_get(t *testing.T) { + request := getRequest("/v3/suppression/bounces") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["start_time"] = "1" + queryParams["end_time"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_bounces_delete(t *testing.T) { + request := getRequest("/v3/suppression/bounces") + request.Method = "DELETE" + request.Body = []byte(` { + "delete_all": true, + "emails": [ + "example@example.com", + "example2@example.com" + ] +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_bounces__email__get(t *testing.T) { + request := getRequest("/v3/suppression/bounces/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_bounces__email__delete(t *testing.T) { + request := getRequest("/v3/suppression/bounces/{email}") + request.Method = "DELETE" + queryParams := make(map[string]string) + queryParams["email_address"] = "example@example.com" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_invalid_emails_get(t *testing.T) { + request := getRequest("/v3/suppression/invalid_emails") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["start_time"] = "1" + queryParams["limit"] = "1" + queryParams["end_time"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_invalid_emails_delete(t *testing.T) { + request := getRequest("/v3/suppression/invalid_emails") + request.Method = "DELETE" + request.Body = []byte(` { + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_invalid_emails__email__get(t *testing.T) { + request := getRequest("/v3/suppression/invalid_emails/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_invalid_emails__email__delete(t *testing.T) { + request := getRequest("/v3/suppression/invalid_emails/{email}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_spam_report__email__get(t *testing.T) { + request := getRequest("/v3/suppression/spam_reports/{email}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_spam_report__email__delete(t *testing.T) { + request := getRequest("/v3/suppression/spam_reports/{email}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_spam_reports_get(t *testing.T) { + request := getRequest("/v3/suppression/spam_reports") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["start_time"] = "1" + queryParams["limit"] = "1" + queryParams["end_time"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_spam_reports_delete(t *testing.T) { + request := getRequest("/v3/suppression/spam_reports") + request.Method = "DELETE" + request.Body = []byte(` { + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_suppression_unsubscribes_get(t *testing.T) { + request := getRequest("/v3/suppression/unsubscribes") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["start_time"] = "1" + queryParams["limit"] = "1" + queryParams["end_time"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates_post(t *testing.T) { + request := getRequest("/v3/templates") + request.Method = "POST" + request.Body = []byte(` { + "name": "example_name" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates_get(t *testing.T) { + request := getRequest("/v3/templates") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__patch(t *testing.T) { + request := getRequest("/v3/templates/{template_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "name": "new_example_name" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__get(t *testing.T) { + request := getRequest("/v3/templates/{template_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__delete(t *testing.T) { + request := getRequest("/v3/templates/{template_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__versions_post(t *testing.T) { + request := getRequest("/v3/templates/{template_id}/versions") + request.Method = "POST" + request.Body = []byte(` { + "active": 1, + "html_content": "<%body%>", + "name": "example_version_name", + "plain_content": "<%body%>", + "subject": "<%subject%>", + "template_id": "ddb96bbc-9b92-425e-8979-99464621b543" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__versions__version_id__patch(t *testing.T) { + request := getRequest("/v3/templates/{template_id}/versions/{version_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "active": 1, + "html_content": "<%body%>", + "name": "updated_example_name", + "plain_content": "<%body%>", + "subject": "<%subject%>" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__versions__version_id__get(t *testing.T) { + request := getRequest("/v3/templates/{template_id}/versions/{version_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__versions__version_id__delete(t *testing.T) { + request := getRequest("/v3/templates/{template_id}/versions/{version_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_templates__template_id__versions__version_id__activate_post(t *testing.T) { + request := getRequest("/v3/templates/{template_id}/versions/{version_id}/activate") + request.Method = "POST" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_get(t *testing.T) { + request := getRequest("/v3/tracking_settings") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_click_patch(t *testing.T) { + request := getRequest("/v3/tracking_settings/click") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_click_get(t *testing.T) { + request := getRequest("/v3/tracking_settings/click") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_google_analytics_patch(t *testing.T) { + request := getRequest("/v3/tracking_settings/google_analytics") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "utm_campaign": "website", + "utm_content": "", + "utm_medium": "email", + "utm_source": "sendgrid.com", + "utm_term": "" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_google_analytics_get(t *testing.T) { + request := getRequest("/v3/tracking_settings/google_analytics") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_open_patch(t *testing.T) { + request := getRequest("/v3/tracking_settings/open") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_open_get(t *testing.T) { + request := getRequest("/v3/tracking_settings/open") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_subscription_patch(t *testing.T) { + request := getRequest("/v3/tracking_settings/subscription") + request.Method = "PATCH" + request.Body = []byte(` { + "enabled": true, + "html_content": "html content", + "landing": "landing page html", + "plain_content": "text content", + "replace": "replacement tag", + "url": "url" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_tracking_settings_subscription_get(t *testing.T) { + request := getRequest("/v3/tracking_settings/subscription") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_account_get(t *testing.T) { + request := getRequest("/v3/user/account") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_credits_get(t *testing.T) { + request := getRequest("/v3/user/credits") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_email_put(t *testing.T) { + request := getRequest("/v3/user/email") + request.Method = "PUT" + request.Body = []byte(` { + "email": "example@example.com" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_email_get(t *testing.T) { + request := getRequest("/v3/user/email") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_password_put(t *testing.T) { + request := getRequest("/v3/user/password") + request.Method = "PUT" + request.Body = []byte(` { + "new_password": "new_password", + "old_password": "old_password" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_profile_patch(t *testing.T) { + request := getRequest("/v3/user/profile") + request.Method = "PATCH" + request.Body = []byte(` { + "city": "Orange", + "first_name": "Example", + "last_name": "User" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_profile_get(t *testing.T) { + request := getRequest("/v3/user/profile") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_scheduled_sends_post(t *testing.T) { + request := getRequest("/v3/user/scheduled_sends") + request.Method = "POST" + request.Body = []byte(` { + "batch_id": "YOUR_BATCH_ID", + "status": "pause" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_scheduled_sends_get(t *testing.T) { + request := getRequest("/v3/user/scheduled_sends") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_scheduled_sends__batch_id__patch(t *testing.T) { + request := getRequest("/v3/user/scheduled_sends/{batch_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "status": "pause" +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_scheduled_sends__batch_id__get(t *testing.T) { + request := getRequest("/v3/user/scheduled_sends/{batch_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_scheduled_sends__batch_id__delete(t *testing.T) { + request := getRequest("/v3/user/scheduled_sends/{batch_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_settings_enforced_tls_patch(t *testing.T) { + request := getRequest("/v3/user/settings/enforced_tls") + request.Method = "PATCH" + request.Body = []byte(` { + "require_tls": true, + "require_valid_cert": false +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_settings_enforced_tls_get(t *testing.T) { + request := getRequest("/v3/user/settings/enforced_tls") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_username_put(t *testing.T) { + request := getRequest("/v3/user/username") + request.Method = "PUT" + request.Body = []byte(` { + "username": "test_username" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_username_get(t *testing.T) { + request := getRequest("/v3/user/username") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_event_settings_patch(t *testing.T) { + request := getRequest("/v3/user/webhooks/event/settings") + request.Method = "PATCH" + request.Body = []byte(` { + "bounce": true, + "click": true, + "deferred": true, + "delivered": true, + "dropped": true, + "enabled": true, + "group_resubscribe": true, + "group_unsubscribe": true, + "open": true, + "processed": true, + "spam_report": true, + "unsubscribe": true, + "url": "url" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_event_settings_get(t *testing.T) { + request := getRequest("/v3/user/webhooks/event/settings") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_event_test_post(t *testing.T) { + request := getRequest("/v3/user/webhooks/event/test") + request.Method = "POST" + request.Body = []byte(` { + "url": "url" +}`) + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_settings_post(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/settings") + request.Method = "POST" + request.Body = []byte(` { + "hostname": "myhostname.com", + "send_raw": false, + "spam_check": true, + "url": "http://email.myhosthame.com" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_settings_get(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/settings") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_settings__hostname__patch(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/settings/{hostname}") + request.Method = "PATCH" + request.Body = []byte(` { + "send_raw": true, + "spam_check": false, + "url": "http://newdomain.com/parse" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_settings__hostname__get(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/settings/{hostname}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_settings__hostname__delete(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/settings/{hostname}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_user_webhooks_parse_stats_get(t *testing.T) { + request := getRequest("/v3/user/webhooks/parse/stats") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["aggregated_by"] = "day" + queryParams["limit"] = "test_string" + queryParams["start_date"] = "2016-01-01" + queryParams["end_date"] = "2016-04-01" + queryParams["offset"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains_post(t *testing.T) { + request := getRequest("/v3/whitelabel/domains") + request.Method = "POST" + request.Body = []byte(` { + "automatic_security": false, + "custom_spf": true, + "default": true, + "domain": "example.com", + "ips": [ + "192.168.1.1", + "192.168.1.2" + ], + "subdomain": "news", + "username": "john@example.com" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains_get(t *testing.T) { + request := getRequest("/v3/whitelabel/domains") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["username"] = "test_string" + queryParams["domain"] = "test_string" + queryParams["exclude_subusers"] = "true" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains_default_get(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/default") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains_subuser_get(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/subuser") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains_subuser_delete(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/subuser") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__domain_id__patch(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{domain_id}") + request.Method = "PATCH" + request.Body = []byte(` { + "custom_spf": true, + "default": false +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__domain_id__get(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{domain_id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__domain_id__delete(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{domain_id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__domain_id__subuser_post(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{domain_id}/subuser") + request.Method = "POST" + request.Body = []byte(` { + "username": "jane@example.com" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__id__ips_post(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{id}/ips") + request.Method = "POST" + request.Body = []byte(` { + "ip": "192.168.0.1" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__id__ips__ip__delete(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{id}/ips/{ip}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_domains__id__validate_post(t *testing.T) { + request := getRequest("/v3/whitelabel/domains/{id}/validate") + request.Method = "POST" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_ips_post(t *testing.T) { + request := getRequest("/v3/whitelabel/ips") + request.Method = "POST" + request.Body = []byte(` { + "domain": "example.com", + "ip": "192.168.1.1", + "subdomain": "email" +}`) + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_ips_get(t *testing.T) { + request := getRequest("/v3/whitelabel/ips") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["ip"] = "test_string" + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_ips__id__get(t *testing.T) { + request := getRequest("/v3/whitelabel/ips/{id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_ips__id__delete(t *testing.T) { + request := getRequest("/v3/whitelabel/ips/{id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_ips__id__validate_post(t *testing.T) { + request := getRequest("/v3/whitelabel/ips/{id}/validate") + request.Method = "POST" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links_post(t *testing.T) { + request := getRequest("/v3/whitelabel/links") + request.Method = "POST" + request.Body = []byte(` { + "default": true, + "domain": "example.com", + "subdomain": "mail" +}`) + queryParams := make(map[string]string) + queryParams["limit"] = "1" + queryParams["offset"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "201" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 201, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links_get(t *testing.T) { + request := getRequest("/v3/whitelabel/links") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["limit"] = "1" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links_default_get(t *testing.T) { + request := getRequest("/v3/whitelabel/links/default") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["domain"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links_subuser_get(t *testing.T) { + request := getRequest("/v3/whitelabel/links/subuser") + request.Method = "GET" + queryParams := make(map[string]string) + queryParams["username"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links_subuser_delete(t *testing.T) { + request := getRequest("/v3/whitelabel/links/subuser") + request.Method = "DELETE" + queryParams := make(map[string]string) + queryParams["username"] = "test_string" + request.QueryParams = queryParams + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links__id__patch(t *testing.T) { + request := getRequest("/v3/whitelabel/links/{id}") + request.Method = "PATCH" + request.Body = []byte(` { + "default": true +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links__id__get(t *testing.T) { + request := getRequest("/v3/whitelabel/links/{id}") + request.Method = "GET" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links__id__delete(t *testing.T) { + request := getRequest("/v3/whitelabel/links/{id}") + request.Method = "DELETE" + request.Headers["X-Mock"] = "204" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 204, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links__id__validate_post(t *testing.T) { + request := getRequest("/v3/whitelabel/links/{id}/validate") + request.Method = "POST" + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} + +func Test_test_whitelabel_links__link_id__subuser_post(t *testing.T) { + request := getRequest("/v3/whitelabel/links/{link_id}/subuser") + request.Method = "POST" + request.Body = []byte(` { + "username": "jane@example.com" +}`) + request.Headers["X-Mock"] = "200" + response, err := MakeRequest(request) + if err != nil { + t.Log(err) + } + assert.Equal(t, 200, response.StatusCode, "Wrong status code returned") +} diff --git a/v3/twilio_email.go b/v3/twilio_email.go new file mode 100644 index 00000000..52981a41 --- /dev/null +++ b/v3/twilio_email.go @@ -0,0 +1,41 @@ +package sendgrid + +import ( + "encoding/base64" + + "github.com/sendgrid/rest" +) + +// TwilioEmailOptions for GetTwilioEmailRequest +type TwilioEmailOptions struct { + Username string + Password string + Endpoint string + Host string +} + +// NewTwilioEmailSendClient constructs a new Twilio Email client given a username and password +func NewTwilioEmailSendClient(username, password string) *Client { + request := GetTwilioEmailRequest(TwilioEmailOptions{Username: username, Password: password, Endpoint: "/v3/mail/send"}) + request.Method = "POST" + return &Client{request} +} + +// GetTwilioEmailRequest create Request +// @return [Request] a default request object +func GetTwilioEmailRequest(twilioEmailOptions TwilioEmailOptions) rest.Request { + credentials := twilioEmailOptions.Username + ":" + twilioEmailOptions.Password + encodedCreds := base64.StdEncoding.EncodeToString([]byte(credentials)) + + options := options{ + Auth: "Basic " + encodedCreds, + Endpoint: twilioEmailOptions.Endpoint, + Host: twilioEmailOptions.Host, + } + + if options.Host == "" { + options.Host = "https://email.twilio.com" + } + + return requestNew(options) +} diff --git a/v3/twilio_email_test.go b/v3/twilio_email_test.go new file mode 100644 index 00000000..51f392c6 --- /dev/null +++ b/v3/twilio_email_test.go @@ -0,0 +1,34 @@ +package sendgrid + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewTwilioEmailSendClient(t *testing.T) { + mailClient := NewTwilioEmailSendClient("username", "password") + assert.Equal(t, "https://email.twilio.com/v3/mail/send", mailClient.BaseURL) + assert.Equal(t, "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", mailClient.Headers["Authorization"]) +} + +func TestGetTwilioEmailRequest(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = fmt.Fprintln(w, "{\"message\": \"success\"}") + })) + defer fakeServer.Close() + + request := GetTwilioEmailRequest(TwilioEmailOptions{ + Username: "username", + Password: "password", + Host: fakeServer.URL, + }) + response, err := MakeRequest(request) + require.NoError(t, err) + assert.True(t, strings.Contains(response.Body, "success")) +}