diff --git a/analytics.go b/analytics.go index 41e00534..a82825fa 100644 --- a/analytics.go +++ b/analytics.go @@ -4,20 +4,10 @@ import ( "context" "github.com/mailgun/errors" + "github.com/mailgun/mailgun-go/v4/mtypes" ) -type MetricsPagination struct { - // Colon-separated value indicating column name and sort direction e.g. 'domain:asc'. - Sort string `json:"sort"` - // The number of items to skip over when satisfying the request. - // To get the first page of data set skip to zero. - // Then increment the skip by the limit for subsequent calls. - Skip int `json:"skip"` - // The maximum number of items returned in the response. - Limit int `json:"limit"` - // The total number of items in the query result set. - Total int `json:"total"` -} +type MetricsOptions = mtypes.MetricsRequest // ListMetrics returns domain/account metrics. // @@ -58,7 +48,7 @@ func (iter *MetricsIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there are // no more pages to retrieve or if there was an error. // Use `.Err()` to retrieve the error -func (iter *MetricsIterator) Next(ctx context.Context, resp *MetricsResponse) (more bool) { +func (iter *MetricsIterator) Next(ctx context.Context, resp *mtypes.MetricsResponse) (more bool) { if iter.err != nil { return false } @@ -73,7 +63,7 @@ func (iter *MetricsIterator) Next(ctx context.Context, resp *MetricsResponse) (m return len(resp.Items) == iter.opts.Pagination.Limit } -func (iter *MetricsIterator) fetch(ctx context.Context, resp *MetricsResponse) error { +func (iter *MetricsIterator) fetch(ctx context.Context, resp *mtypes.MetricsResponse) error { if resp == nil { return errors.New("resp cannot be nil") } @@ -86,7 +76,7 @@ func (iter *MetricsIterator) fetch(ctx context.Context, resp *MetricsResponse) e } // preallocate - resp.Items = make([]MetricsItem, 0, iter.opts.Pagination.Limit) + resp.Items = make([]mtypes.MetricsItem, 0, iter.opts.Pagination.Limit) err = httpResp.parseFromJSON(resp) if err != nil { diff --git a/analytics_test.go b/analytics_test.go index dcb81d5a..970600b6 100644 --- a/analytics_test.go +++ b/analytics_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,37 +15,37 @@ func TestListMetrics(t *testing.T) { err := mg.SetAPIBase(server.URL()) require.NoError(t, err) - start, _ := mailgun.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000") - end, _ := mailgun.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000") + start, _ := mtypes.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000") + end, _ := mtypes.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000") - opts := mailgun.MetricsOptions{ + opts := mtypes.MetricsRequest{ Start: start, End: end, - Pagination: mailgun.MetricsPagination{ + Pagination: mtypes.MetricsPagination{ Limit: 10, }, } // filter by domain - opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{ + opts.Filter.BoolGroupAnd = []mtypes.MetricsFilterPredicate{{ Attribute: "domain", Comparator: "=", - LabeledValues: []mailgun.MetricsLabeledValue{{Label: testDomain, Value: testDomain}}, + LabeledValues: []mtypes.MetricsLabeledValue{{Label: testDomain, Value: testDomain}}, }} - wantResp := mailgun.MetricsResponse{ + wantResp := mtypes.MetricsResponse{ Start: start, End: end, Resolution: "day", Duration: "30d", Dimensions: []string{"time"}, - Items: []mailgun.MetricsItem{ + Items: []mtypes.MetricsItem{ { - Dimensions: []mailgun.MetricsDimension{{ + Dimensions: []mtypes.MetricsDimension{{ Dimension: "time", Value: "Tue, 24 Sep 2024 00:00:00 +0000", DisplayValue: "Tue, 24 Sep 2024 00:00:00 +0000", }}, - Metrics: mailgun.Metrics{ + Metrics: mtypes.Metrics{ SentCount: ptr(uint64(4)), DeliveredCount: ptr(uint64(3)), OpenedCount: ptr(uint64(2)), @@ -52,7 +53,7 @@ func TestListMetrics(t *testing.T) { }, }, }, - Pagination: mailgun.MetricsPagination{ + Pagination: mtypes.MetricsPagination{ Sort: "", Skip: 0, Limit: 10, @@ -63,7 +64,7 @@ func TestListMetrics(t *testing.T) { it, err := mg.ListMetrics(opts) require.NoError(t, err) - var page mailgun.MetricsResponse + var page mtypes.MetricsResponse ctx := context.Background() more := it.Next(ctx, &page) require.Nil(t, it.Err()) diff --git a/attachments_test.go b/attachments_test.go index 964f8d8a..9df5fb35 100644 --- a/attachments_test.go +++ b/attachments_test.go @@ -42,7 +42,7 @@ func TestMultipleAttachments(t *testing.T) { require.NoError(t, err) id = strings.Trim(id, "<>") - t.Logf("New Email: %s Id: %s\n", msg, id) + t.Logf("New Email: %s ID: %s\n", msg, id) e, err := findAcceptedMessage(mg, id) require.NoError(t, err) diff --git a/bounces.go b/bounces.go index cba67137..3fdce1ec 100644 --- a/bounces.go +++ b/bounces.go @@ -3,31 +3,13 @@ package mailgun import ( "context" "strconv" -) - -// Bounce aggregates data relating to undeliverable messages to a specific intended recipient, -// identified by Address. -type Bounce struct { - // The time at which Mailgun detected the bounce. - CreatedAt RFC2822Time `json:"created_at"` - // Code provides the SMTP error code that caused the bounce - Code string `json:"code"` - // Address the bounce is for - Address string `json:"address"` - // human readable reason why - Error string `json:"error"` -} -type Paging struct { - First string `json:"first,omitempty"` - Next string `json:"next,omitempty"` - Previous string `json:"previous,omitempty"` - Last string `json:"last,omitempty"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) type bouncesListResponse struct { - Items []Bounce `json:"items"` - Paging Paging `json:"paging"` + Items []mtypes.Bounce `json:"items"` + Paging mtypes.Paging `json:"paging"` } // ListBounces returns a complete set of bounces logged against the sender's domain, if any. @@ -46,7 +28,7 @@ func (mg *MailgunImpl) ListBounces(domain string, opts *ListOptions) *BouncesIte url, err := r.generateUrlWithParameters() return &BouncesIterator{ mg: mg, - bouncesListResponse: bouncesListResponse{Paging: Paging{Next: url, First: url}}, + bouncesListResponse: bouncesListResponse{Paging: mtypes.Paging{Next: url, First: url}}, err: err, } } @@ -57,7 +39,7 @@ type BouncesIterator struct { err error } -// If an error occurred during iteration `Err()` will return non nil +// Err if an error occurred during iteration `Err()` will return non nil func (ci *BouncesIterator) Err() error { return ci.err } @@ -65,7 +47,7 @@ func (ci *BouncesIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool { +func (ci *BouncesIterator) Next(ctx context.Context, items *[]mtypes.Bounce) bool { if ci.err != nil { return false } @@ -73,7 +55,7 @@ func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool { if ci.err != nil { return false } - cpy := make([]Bounce, len(ci.Items)) + cpy := make([]mtypes.Bounce, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -83,7 +65,7 @@ func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool { +func (ci *BouncesIterator) First(ctx context.Context, items *[]mtypes.Bounce) bool { if ci.err != nil { return false } @@ -91,7 +73,7 @@ func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool { if ci.err != nil { return false } - cpy := make([]Bounce, len(ci.Items)) + cpy := make([]mtypes.Bounce, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -101,7 +83,7 @@ func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool { // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool { +func (ci *BouncesIterator) Last(ctx context.Context, items *[]mtypes.Bounce) bool { if ci.err != nil { return false } @@ -109,7 +91,7 @@ func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool { if ci.err != nil { return false } - cpy := make([]Bounce, len(ci.Items)) + cpy := make([]mtypes.Bounce, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -118,7 +100,7 @@ func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool { +func (ci *BouncesIterator) Previous(ctx context.Context, items *[]mtypes.Bounce) bool { if ci.err != nil { return false } @@ -129,7 +111,7 @@ func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool { if ci.err != nil { return false } - cpy := make([]Bounce, len(ci.Items)) + cpy := make([]mtypes.Bounce, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -146,12 +128,12 @@ func (ci *BouncesIterator) fetch(ctx context.Context, url string) error { } // GetBounce retrieves a single bounce record, if any exist, for the given recipient address. -func (mg *MailgunImpl) GetBounce(ctx context.Context, domain, address string) (Bounce, error) { +func (mg *MailgunImpl) GetBounce(ctx context.Context, domain, address string) (mtypes.Bounce, error) { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, bouncesEndpoint, domain) + "/" + address) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var response Bounce + var response mtypes.Bounce err := getResponseFromJSON(ctx, r, &response) return response, err } @@ -190,7 +172,7 @@ func (mg *MailgunImpl) AddBounce(ctx context.Context, domain, address, code, bou } // Add Bounces adds a list of bounces to the bounce list -func (mg *MailgunImpl) AddBounces(ctx context.Context, domain string, bounces []Bounce) error { +func (mg *MailgunImpl) AddBounces(ctx context.Context, domain string, bounces []mtypes.Bounce) error { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, bouncesEndpoint, domain)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) diff --git a/bounces_test.go b/bounces_test.go index 9329f45a..489b0d90 100644 --- a/bounces_test.go +++ b/bounces_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/require" ) @@ -21,7 +22,7 @@ func TestGetBounces(t *testing.T) { ctx := context.Background() it := mg.ListBounces(testDomain, nil) - var page []mailgun.Bounce + var page []mtypes.Bounce for it.Next(ctx, &page) { for _, bounce := range page { t.Logf("Bounce: %+v\n", bounce) @@ -55,7 +56,7 @@ func TestAddDelBounces(t *testing.T) { findBounce := func(address string) bool { it := mg.ListBounces(testDomain, nil) - var page []mailgun.Bounce + var page []mtypes.Bounce for it.Next(ctx, &page) { require.True(t, len(page) != 0) for _, bounce := range page { @@ -115,7 +116,7 @@ func TestAddDelBounceList(t *testing.T) { findBounce := func(address string) bool { it := mg.ListBounces(testDomain, nil) - var page []mailgun.Bounce + var page []mtypes.Bounce for it.Next(ctx, &page) { require.True(t, len(page) != 0) for _, bounce := range page { @@ -131,13 +132,13 @@ func TestAddDelBounceList(t *testing.T) { return false } - createdAt, err := mailgun.NewRFC2822Time("Thu, 13 Oct 2011 18:02:00 +0000") + createdAt, err := mtypes.NewRFC2822Time("Thu, 13 Oct 2011 18:02:00 +0000") if err != nil { t.Fatalf("invalid time") } // Generate a list of bounces - bounces := []mailgun.Bounce{ + bounces := []mtypes.Bounce{ { Code: "550", Address: fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain), @@ -176,7 +177,7 @@ func TestAddDelBounceList(t *testing.T) { require.NoError(t, err) it := mg.ListBounces(testDomain, nil) - var page []mailgun.Bounce + var page []mtypes.Bounce if it.Next(ctx, &page) { t.Fatalf("Expected no item in the bounce list") } diff --git a/credentials.go b/credentials.go index 945a9c70..2616d95a 100644 --- a/credentials.go +++ b/credentials.go @@ -2,25 +2,20 @@ package mailgun import ( "context" - "fmt" + "errors" "strconv" -) -// A Credential structure describes a principle allowed to send or receive mail at the domain. -type Credential struct { - CreatedAt RFC2822Time `json:"created_at"` - Login string `json:"login"` - Password string `json:"password"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) type credentialsListResponse struct { // is -1 if Next() or First() have not been called - TotalCount int `json:"total_count"` - Items []Credential `json:"items"` + TotalCount int `json:"total_count"` + Items []mtypes.Credential `json:"items"` } -// Returned when a required parameter is missing. -var ErrEmptyParam = fmt.Errorf("empty or illegal parameter") +// ErrEmptyParam is returned when a required parameter is missing. +var ErrEmptyParam = errors.New("empty or illegal parameter") // ListCredentials returns the (possibly zero-length) list of credentials associated with your domain. func (mg *MailgunImpl) ListCredentials(domain string, opts *ListOptions) *CredentialsIterator { @@ -63,7 +58,7 @@ func (ri *CredentialsIterator) Offset() int { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bool { +func (ri *CredentialsIterator) Next(ctx context.Context, items *[]mtypes.Credential) bool { if ri.err != nil { return false } @@ -73,7 +68,7 @@ func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bo return false } - cpy := make([]Credential, len(ri.Items)) + cpy := make([]mtypes.Credential, len(ri.Items)) copy(cpy, ri.Items) *items = cpy if len(ri.Items) == 0 { @@ -86,7 +81,7 @@ func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bo // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) bool { +func (ri *CredentialsIterator) First(ctx context.Context, items *[]mtypes.Credential) bool { if ri.err != nil { return false } @@ -94,7 +89,7 @@ func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) b if ri.err != nil { return false } - cpy := make([]Credential, len(ri.Items)) + cpy := make([]mtypes.Credential, len(ri.Items)) copy(cpy, ri.Items) *items = cpy ri.offset = len(ri.Items) @@ -105,7 +100,7 @@ func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) b // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bool { +func (ri *CredentialsIterator) Last(ctx context.Context, items *[]mtypes.Credential) bool { if ri.err != nil { return false } @@ -123,7 +118,7 @@ func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bo if ri.err != nil { return false } - cpy := make([]Credential, len(ri.Items)) + cpy := make([]mtypes.Credential, len(ri.Items)) copy(cpy, ri.Items) *items = cpy return true @@ -132,7 +127,7 @@ func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bo // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]Credential) bool { +func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]mtypes.Credential) bool { if ri.err != nil { return false } @@ -150,7 +145,7 @@ func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]Credential if ri.err != nil { return false } - cpy := make([]Credential, len(ri.Items)) + cpy := make([]mtypes.Credential, len(ri.Items)) copy(cpy, ri.Items) *items = cpy diff --git a/credentials_test.go b/credentials_test.go index 8d81106c..cb2ccac1 100644 --- a/credentials_test.go +++ b/credentials_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/require" ) @@ -18,7 +19,7 @@ func TestGetCredentials(t *testing.T) { ctx := context.Background() it := mg.ListCredentials(testDomain, nil) - var page []mailgun.Credential + var page []mtypes.Credential for it.Next(ctx, &page) { t.Logf("Login\tCreated At\t\n") for _, c := range page { diff --git a/domains.go b/domains.go index 6ed4c4e0..142565bc 100644 --- a/domains.go +++ b/domains.go @@ -4,92 +4,10 @@ import ( "context" "strconv" "strings" -) -// Use these to specify a spam action when creating a new domain. -const ( - // SpamActionTag tags the received message with headers providing a measure of its spamness. - SpamActionTag = SpamAction("tag") - // SpamActionDisabled prevents Mailgun from taking any action on what it perceives to be spam. - SpamActionDisabled = SpamAction("disabled") - // SpamActionDelete instructs Mailgun to just block or delete the message all-together. - SpamActionDelete = SpamAction("delete") + "github.com/mailgun/mailgun-go/v4/mtypes" ) -type SpamAction string - -// A Domain structure holds information about a domain used when sending mail. -type Domain struct { - CreatedAt RFC2822Time `json:"created_at"` - ID string `json:"id"` - IsDisabled bool `json:"is_disabled"` - Name string `json:"name"` - RequireTLS bool `json:"require_tls"` - SkipVerification bool `json:"skip_verification"` - SMTPLogin string `json:"smtp_login"` - SMTPPassword string `json:"smtp_password,omitempty"` - SpamAction SpamAction `json:"spam_action"` - State string `json:"state"` - Type string `json:"type"` - TrackingHost string `json:"tracking_host,omitempty"` - UseAutomaticSenderSecurity bool `json:"use_automatic_sender_security"` - WebPrefix string `json:"web_prefix"` - WebScheme string `json:"web_scheme"` - Wildcard bool `json:"wildcard"` -} - -// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun. -// Note that Mailgun does not host DNS records. -type DNSRecord struct { - Active bool `json:"is_active"` - Cached []string `json:"cached"` - Name string `json:"name,omitempty"` - Priority string `json:"priority,omitempty"` - RecordType string `json:"record_type"` - Valid string `json:"valid"` - Value string `json:"value"` -} - -type GetDomainResponse struct { - Domain Domain `json:"domain"` - ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"` - SendingDNSRecords []DNSRecord `json:"sending_dns_records"` -} - -type domainConnectionResponse struct { - Connection DomainConnection `json:"connection"` -} - -type domainsListResponse struct { - // is -1 if Next() or First() have not been called - TotalCount int `json:"total_count"` - Items []Domain `json:"items"` -} - -// Specify the domain connection options -type DomainConnection struct { - RequireTLS bool `json:"require_tls"` - SkipVerification bool `json:"skip_verification"` -} - -// Specify the domain tracking options -type DomainTracking struct { - Click TrackingStatus `json:"click"` - Open TrackingStatus `json:"open"` - Unsubscribe TrackingStatus `json:"unsubscribe"` -} - -// The tracking status of a domain -type TrackingStatus struct { - Active bool `json:"active"` - HTMLFooter string `json:"html_footer"` - TextFooter string `json:"text_footer"` -} - -type domainTrackingResponse struct { - Tracking DomainTracking `json:"tracking"` -} - type ListDomainsOptions struct { Limit int } @@ -107,13 +25,13 @@ func (mg *MailgunImpl) ListDomains(opts *ListDomainsOptions) *DomainsIterator { return &DomainsIterator{ mg: mg, url: generateApiUrl(mg, 4, domainsEndpoint), - domainsListResponse: domainsListResponse{TotalCount: -1}, + ListDomainsResponse: mtypes.ListDomainsResponse{TotalCount: -1}, limit: limit, } } type DomainsIterator struct { - domainsListResponse + mtypes.ListDomainsResponse limit int mg Mailgun @@ -135,7 +53,7 @@ func (ri *DomainsIterator) Offset() int { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool { +func (ri *DomainsIterator) Next(ctx context.Context, items *[]mtypes.Domain) bool { if ri.err != nil { return false } @@ -145,7 +63,7 @@ func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool { return false } - cpy := make([]Domain, len(ri.Items)) + cpy := make([]mtypes.Domain, len(ri.Items)) copy(cpy, ri.Items) *items = cpy if len(ri.Items) == 0 { @@ -158,7 +76,7 @@ func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool { +func (ri *DomainsIterator) First(ctx context.Context, items *[]mtypes.Domain) bool { if ri.err != nil { return false } @@ -166,7 +84,7 @@ func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool { if ri.err != nil { return false } - cpy := make([]Domain, len(ri.Items)) + cpy := make([]mtypes.Domain, len(ri.Items)) copy(cpy, ri.Items) *items = cpy ri.offset = len(ri.Items) @@ -177,7 +95,7 @@ func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool { // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool { +func (ri *DomainsIterator) Last(ctx context.Context, items *[]mtypes.Domain) bool { if ri.err != nil { return false } @@ -195,7 +113,7 @@ func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool { if ri.err != nil { return false } - cpy := make([]Domain, len(ri.Items)) + cpy := make([]mtypes.Domain, len(ri.Items)) copy(cpy, ri.Items) *items = cpy return true @@ -204,7 +122,7 @@ func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool { +func (ri *DomainsIterator) Previous(ctx context.Context, items *[]mtypes.Domain) bool { if ri.err != nil { return false } @@ -222,7 +140,7 @@ func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool { if ri.err != nil { return false } - cpy := make([]Domain, len(ri.Items)) + cpy := make([]mtypes.Domain, len(ri.Items)) copy(cpy, ri.Items) *items = cpy @@ -242,28 +160,28 @@ func (ri *DomainsIterator) fetch(ctx context.Context, skip, limit int) error { r.addParameter("limit", strconv.Itoa(limit)) } - return getResponseFromJSON(ctx, r, &ri.domainsListResponse) + return getResponseFromJSON(ctx, r, &ri.ListDomainsResponse) } type GetDomainOptions struct{} // GetDomain retrieves detailed information about the named domain. -func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string, _ *GetDomainOptions) (GetDomainResponse, error) { +func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string, _ *GetDomainOptions) (mtypes.GetDomainResponse, error) { r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint) + "/" + domain) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp GetDomainResponse + var resp mtypes.GetDomainResponse err := getResponseFromJSON(ctx, r, &resp) return resp, err } -func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (GetDomainResponse, error) { +func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (mtypes.GetDomainResponse, error) { r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint) + "/" + domain + "/verify") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) payload := newUrlEncodedPayload() - var resp GetDomainResponse + var resp mtypes.GetDomainResponse err := putResponseFromJSON(ctx, r, payload, &resp) return resp, err } @@ -273,7 +191,7 @@ func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (GetDoma // TODO(vtopc): support all fields type CreateDomainOptions struct { Password string - SpamAction SpamAction + SpamAction mtypes.SpamAction Wildcard bool ForceDKIMAuthority bool DKIMKeySize int @@ -287,7 +205,7 @@ type CreateDomainOptions struct { // The spamAction domain must be one of Delete, Tag, or Disabled. // The wildcard parameter instructs Mailgun to treat all subdomains of this domain uniformly if true, // and as different domains if false. -func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (GetDomainResponse, error) { +func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (mtypes.GetDomainResponse, error) { r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -318,7 +236,7 @@ func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *Crea payload.addValue("web_scheme", opts.WebScheme) } } - var resp GetDomainResponse + var resp mtypes.GetDomainResponse err := postResponseFromJSON(ctx, r, payload, &resp) return resp, err } @@ -360,10 +278,3 @@ func (mg *MailgunImpl) UpdateDomain(ctx context.Context, name string, opts *Upda return err } - -func boolToString(b bool) string { - if b { - return "true" - } - return "false" -} diff --git a/domains_connection.go b/domains_connection.go index 6ecbb53a..efca21cf 100644 --- a/domains_connection.go +++ b/domains_connection.go @@ -2,20 +2,22 @@ package mailgun import ( "context" + + "github.com/mailgun/mailgun-go/v4/mtypes" ) // GetDomainConnection returns delivery connection settings for the defined domain -func (mg *MailgunImpl) GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error) { +func (mg *MailgunImpl) GetDomainConnection(ctx context.Context, domain string) (mtypes.DomainConnection, error) { r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/connection") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp domainConnectionResponse + var resp mtypes.DomainConnectionResponse err := getResponseFromJSON(ctx, r, &resp) return resp.Connection, err } // UpdateDomainConnection updates the specified delivery connection settings for the defined domain -func (mg *MailgunImpl) UpdateDomainConnection(ctx context.Context, domain string, settings DomainConnection) error { +func (mg *MailgunImpl) UpdateDomainConnection(ctx context.Context, domain string, settings mtypes.DomainConnection) error { r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/connection") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) diff --git a/domains_test.go b/domains_test.go index f0836bf8..f51a2c8b 100644 --- a/domains_test.go +++ b/domains_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,7 +25,7 @@ func TestListDomains(t *testing.T) { ctx := context.Background() it := mg.ListDomains(nil) - var page []mailgun.Domain + var page []mtypes.Domain for it.Next(ctx, &page) { for _, d := range page { t.Logf("TestListDomains: %#v\n", d) @@ -43,7 +44,7 @@ func TestGetSingleDomain(t *testing.T) { ctx := context.Background() it := mg.ListDomains(nil) - var page []mailgun.Domain + var page []mtypes.Domain require.True(t, it.Next(ctx, &page)) require.NoError(t, it.Err()) @@ -85,7 +86,7 @@ func TestAddUpdateDeleteDomain(t *testing.T) { // First, we need to add the domain. _, err = mg.CreateDomain(ctx, "mx.mailgun.test", - &mailgun.CreateDomainOptions{SpamAction: mailgun.SpamActionTag, Password: "supersecret", WebScheme: "http"}) + &mailgun.CreateDomainOptions{SpamAction: mtypes.SpamActionTag, Password: "supersecret", WebScheme: "http"}) require.NoError(t, err) // Then, we update it. diff --git a/domains_tracking.go b/domains_tracking.go index a0a029d4..3be52c82 100644 --- a/domains_tracking.go +++ b/domains_tracking.go @@ -2,14 +2,16 @@ package mailgun import ( "context" + + "github.com/mailgun/mailgun-go/v4/mtypes" ) // GetDomainTracking returns tracking settings for a domain -func (mg *MailgunImpl) GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error) { +func (mg *MailgunImpl) GetDomainTracking(ctx context.Context, domain string) (mtypes.DomainTracking, error) { r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/tracking") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp domainTrackingResponse + var resp mtypes.DomainTrackingResponse err := getResponseFromJSON(ctx, r, &resp) return resp.Tracking, err } diff --git a/events.go b/events.go index ee311d80..2ec47cd3 100644 --- a/events.go +++ b/events.go @@ -279,8 +279,3 @@ func (ep *EventPoller) Poll(ctx context.Context, ee *[]Event) bool { } } } - -// Given time.Time{} return a float64 as given in mailgun event timestamps -func TimeToFloat(t time.Time) float64 { - return float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000)) -} diff --git a/events_test.go b/events_test.go index f89b3896..b81d22ed 100644 --- a/events_test.go +++ b/events_test.go @@ -93,7 +93,7 @@ func TestEventPoller(t *testing.T) { msg, id, err := mg.Send(ctx, m) require.NoError(t, err) - t.Logf("New Email: %s Id: %s\n", msg, id) + t.Logf("New Email: %s ID: %s\n", msg, id) var accepted *events.Accepted for e := range eventChan { diff --git a/examples/examples.go b/examples/examples.go index 6e1814b3..79ba1da5 100644 --- a/examples/examples.go +++ b/examples/examples.go @@ -8,6 +8,7 @@ import ( "github.com/mailgun/mailgun-go/v4" "github.com/mailgun/mailgun-go/v4/events" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func AddBounce(domain, apiKey string) error { @@ -28,7 +29,7 @@ func CreateComplaint(domain, apiKey string) error { return mg.CreateComplaint(ctx, domain, "bob@example.com") } -func AddDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { +func AddDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -36,7 +37,7 @@ func AddDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { return mg.CreateDomain(ctx, domain, &mailgun.CreateDomainOptions{ Password: "super_secret", - SpamAction: mailgun.SpamActionTag, + SpamAction: mtypes.SpamActionTag, Wildcard: false, }) } @@ -53,10 +54,10 @@ func AddDomainIPS(domain, apiKey string) error { func AddListMember(apiKey string) error { mg := mailgun.NewMailgun(apiKey) - memberJoe := mailgun.Member{ + memberJoe := mtypes.Member{ Address: "joe@example.com", Name: "Joe Example", - Subscribed: mailgun.Subscribed, + Subscribed: mtypes.Subscribed, } ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -72,17 +73,17 @@ func AddListMembers(apiKey string) error { defer cancel() return mg.CreateMemberList(ctx, nil, "mailgunList@example.com", []any{ - mailgun.Member{ + mtypes.Member{ Address: "alice@example.com", Name: "Alice's debugging account", - Subscribed: mailgun.Unsubscribed, + Subscribed: mtypes.Unsubscribed, }, - mailgun.Member{ + mtypes.Member{ Address: "Bob Cool ", Name: "Bob's Cool Account", - Subscribed: mailgun.Subscribed, + Subscribed: mtypes.Subscribed, }, - mailgun.Member{ + mtypes.Member{ Address: "joe.hamradio@example.com", // Charlette is a ham radio packet BBS user. // We attach her packet BBS e-mail address as an arbitrary var here. @@ -108,7 +109,7 @@ func CreateUnsubscribes(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - unsubscribes := []mailgun.Unsubscribe{ + unsubscribes := []mtypes.Unsubscribe{ {Address: "alice@example.com"}, {Address: "bob@example.com", Tags: []string{"tag1"}}, } @@ -152,7 +153,7 @@ func CreateCredential(domain, apiKey string) error { return mg.CreateCredential(ctx, domain, "alice@example.com", "secret") } -func CreateDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { +func CreateDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -160,7 +161,7 @@ func CreateDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { return mg.CreateDomain(ctx, domain, &mailgun.CreateDomainOptions{ Password: "super_secret", - SpamAction: mailgun.SpamActionTag, + SpamAction: mtypes.SpamActionTag, Wildcard: false, }) } @@ -174,27 +175,27 @@ func CreateExport(apiKey string) error { return mg.CreateExport(ctx, "/v3/domains") } -func CreateMailingList(apiKey string) (mailgun.MailingList, error) { +func CreateMailingList(apiKey string) (mtypes.MailingList, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.CreateMailingList(ctx, mailgun.MailingList{ + return mg.CreateMailingList(ctx, mtypes.MailingList{ Address: "list@example.com", Name: "dev", Description: "Mailgun developers list.", - AccessLevel: mailgun.AccessLevelMembers, + AccessLevel: mtypes.AccessLevelMembers, }) } -func CreateRoute(domain, apiKey string) (mailgun.Route, error) { +func CreateRoute(domain, apiKey string) (mtypes.Route, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.CreateRoute(ctx, mailgun.Route{ + return mg.CreateRoute(ctx, mtypes.Route{ Priority: 1, Description: "Sample Route", Expression: "match_recipient(\".*@YOUR_DOMAIN_NAME\")", @@ -336,7 +337,7 @@ func PrintEvents(domain, apiKey string) error { return nil } -func GetBounce(domain, apiKey string) (mailgun.Bounce, error) { +func GetBounce(domain, apiKey string) (mtypes.Bounce, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -345,14 +346,14 @@ func GetBounce(domain, apiKey string) (mailgun.Bounce, error) { return mg.GetBounce(ctx, domain, "foo@bar.com") } -func ListBounces(domain, apiKey string) ([]mailgun.Bounce, error) { +func ListBounces(domain, apiKey string) ([]mtypes.Bounce, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListBounces(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Bounce + var page, result []mtypes.Bounce for it.Next(ctx, &page) { result = append(result, page...) } @@ -363,7 +364,7 @@ func ListBounces(domain, apiKey string) ([]mailgun.Bounce, error) { return result, nil } -func GetComplaints(domain, apiKey string) (mailgun.Complaint, error) { +func GetComplaints(domain, apiKey string) (mtypes.Complaint, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -372,14 +373,14 @@ func GetComplaints(domain, apiKey string) (mailgun.Complaint, error) { return mg.GetComplaint(ctx, domain, "baz@example.com") } -func ListComplaints(domain, apiKey string) ([]mailgun.Complaint, error) { +func ListComplaints(domain, apiKey string) ([]mtypes.Complaint, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListComplaints(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Complaint + var page, result []mtypes.Complaint for it.Next(ctx, &page) { result = append(result, page...) } @@ -390,7 +391,7 @@ func ListComplaints(domain, apiKey string) ([]mailgun.Complaint, error) { return result, nil } -func GetDomainConnection(domain, apiKey string) (mailgun.DomainConnection, error) { +func GetDomainConnection(domain, apiKey string) (mtypes.DomainConnection, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -399,14 +400,14 @@ func GetDomainConnection(domain, apiKey string) (mailgun.DomainConnection, error return mg.GetDomainConnection(ctx, domain) } -func ListCredentials(domain, apiKey string) ([]mailgun.Credential, error) { +func ListCredentials(domain, apiKey string) ([]mtypes.Credential, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListCredentials(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Credential + var page, result []mtypes.Credential for it.Next(ctx, &page) { result = append(result, page...) } @@ -417,7 +418,7 @@ func ListCredentials(domain, apiKey string) ([]mailgun.Credential, error) { return result, nil } -func GetDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { +func GetDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -426,7 +427,7 @@ func GetDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) { return mg.GetDomain(ctx, domain, nil) } -func ListDomainIPS(domain, apiKey string) ([]mailgun.IPAddress, error) { +func ListDomainIPS(domain, apiKey string) ([]mtypes.IPAddress, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -435,7 +436,7 @@ func ListDomainIPS(domain, apiKey string) ([]mailgun.IPAddress, error) { return mg.ListDomainIPS(ctx, domain) } -func GetDomainTracking(domain, apiKey string) (mailgun.DomainTracking, error) { +func GetDomainTracking(domain, apiKey string) (mtypes.DomainTracking, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -444,14 +445,14 @@ func GetDomainTracking(domain, apiKey string) (mailgun.DomainTracking, error) { return mg.GetDomainTracking(ctx, domain) } -func ListDomains(domain, apiKey string) ([]mailgun.Domain, error) { +func ListDomains(domain, apiKey string) ([]mtypes.Domain, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListDomains(nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Domain + var page, result []mtypes.Domain for it.Next(ctx, &page) { result = append(result, page...) } @@ -462,7 +463,7 @@ func ListDomains(domain, apiKey string) ([]mailgun.Domain, error) { return result, nil } -func GetExport(domain, apiKey string) (mailgun.Export, error) { +func GetExport(domain, apiKey string) (mtypes.Export, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -471,7 +472,7 @@ func GetExport(domain, apiKey string) (mailgun.Export, error) { return mg.GetExport(ctx, "EXPORT_ID") } -func GetIP(domain, apiKey string) (mailgun.IPAddress, error) { +func GetIP(domain, apiKey string) (mtypes.IPAddress, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -480,7 +481,7 @@ func GetIP(domain, apiKey string) (mailgun.IPAddress, error) { return mg.GetIP(ctx, "127.0.0.1") } -func ListIPS(domain, apiKey string) ([]mailgun.IPAddress, error) { +func ListIPS(domain, apiKey string) ([]mtypes.IPAddress, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -490,7 +491,7 @@ func ListIPS(domain, apiKey string) ([]mailgun.IPAddress, error) { return mg.ListIPS(ctx, true) } -func GetTagLimits(domain, apiKey string) (mailgun.TagLimits, error) { +func GetTagLimits(domain, apiKey string) (mtypes.TagLimits, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -499,7 +500,7 @@ func GetTagLimits(domain, apiKey string) (mailgun.TagLimits, error) { return mg.GetTagLimits(ctx, domain) } -func ListExports(apiKey string) ([]mailgun.Export, error) { +func ListExports(apiKey string) ([]mtypes.Export, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -509,14 +510,14 @@ func ListExports(apiKey string) ([]mailgun.Export, error) { return mg.ListExports(ctx, "") } -func GetMembers(apiKey string) ([]mailgun.Member, error) { +func GetMembers(apiKey string) ([]mtypes.Member, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListMembers("list@example.com", nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Member + var page, result []mtypes.Member for it.Next(ctx, &page) { result = append(result, page...) } @@ -527,14 +528,14 @@ func GetMembers(apiKey string) ([]mailgun.Member, error) { return result, nil } -func ListMailingLists(apiKey string) ([]mailgun.MailingList, error) { +func ListMailingLists(apiKey string) ([]mtypes.MailingList, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListMailingLists(nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.MailingList + var page, result []mtypes.MailingList for it.Next(ctx, &page) { result = append(result, page...) } @@ -545,7 +546,7 @@ func ListMailingLists(apiKey string) ([]mailgun.MailingList, error) { return result, nil } -func GetRoute(domain, apiKey string) (mailgun.Route, error) { +func GetRoute(domain, apiKey string) (mtypes.Route, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -554,14 +555,14 @@ func GetRoute(domain, apiKey string) (mailgun.Route, error) { return mg.GetRoute(ctx, "route_id") } -func ListRoutes(domain, apiKey string) ([]mailgun.Route, error) { +func ListRoutes(domain, apiKey string) ([]mtypes.Route, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListRoutes(nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Route + var page, result []mtypes.Route for it.Next(ctx, &page) { result = append(result, page...) } @@ -572,14 +573,14 @@ func ListRoutes(domain, apiKey string) ([]mailgun.Route, error) { return result, nil } -func ListTags(domain, apiKey string) ([]mailgun.Tag, error) { +func ListTags(domain, apiKey string) ([]mtypes.Tag, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListTags(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Tag + var page, result []mtypes.Tag for it.Next(ctx, &page) { result = append(result, page...) } @@ -590,14 +591,14 @@ func ListTags(domain, apiKey string) ([]mailgun.Tag, error) { return result, nil } -func ListUnsubscribes(domain, apiKey string) ([]mailgun.Unsubscribe, error) { +func ListUnsubscribes(domain, apiKey string) ([]mtypes.Unsubscribe, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListUnsubscribes(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Unsubscribe + var page, result []mtypes.Unsubscribe for it.Next(ctx, &page) { result = append(result, page...) } @@ -608,7 +609,7 @@ func ListUnsubscribes(domain, apiKey string) ([]mailgun.Unsubscribe, error) { return result, nil } -func ValidateEmail(apiKey string) (mailgun.ValidateEmailResponse, error) { +func ValidateEmail(apiKey string) (mtypes.ValidateEmailResponse, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -860,7 +861,7 @@ func UpdateDomainConnection(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.UpdateDomainConnection(ctx, domain, mailgun.DomainConnection{ + return mg.UpdateDomainConnection(ctx, domain, mtypes.DomainConnection{ RequireTLS: true, SkipVerification: true, }) @@ -872,9 +873,9 @@ func UpdateMember(apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - _, err := mg.UpdateMember(ctx, "bar@example.com", "list@example.com", mailgun.Member{ + _, err := mg.UpdateMember(ctx, "bar@example.com", "list@example.com", mtypes.Member{ Name: "Foo Bar", - Subscribed: mailgun.Unsubscribed, + Subscribed: mtypes.Unsubscribed, }) return err } @@ -892,7 +893,7 @@ func VerifyWebhookSignature(apiKey, webhookSigningKey, timestamp, token, signatu mg := mailgun.NewMailgun(apiKey) mg.SetWebhookSigningKey(webhookSigningKey) - return mg.VerifyWebhookSignature(mailgun.Signature{ + return mg.VerifyWebhookSignature(mtypes.Signature{ TimeStamp: timestamp, Token: token, Signature: signature, @@ -907,11 +908,11 @@ func SendMessageWithTemplate(domain, apiKey string) error { defer cancel() // Create a new template - err = mg.CreateTemplate(ctx, domain, &mailgun.Template{ + err = mg.CreateTemplate(ctx, domain, &mtypes.Template{ Name: "my-template", - Version: mailgun.TemplateVersion{ + Version: mtypes.TemplateVersion{ Template: `'

{{.title}}

{{.body}}
'`, - Engine: mailgun.TemplateEngineGo, + Engine: mtypes.TemplateEngineGo, Tag: "v1", }, }) @@ -945,11 +946,11 @@ func CreateTemplate(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.CreateTemplate(ctx, domain, &mailgun.Template{ + return mg.CreateTemplate(ctx, domain, &mtypes.Template{ Name: "my-template", - Version: mailgun.TemplateVersion{ + Version: mtypes.TemplateVersion{ Template: `'

{{.title}}

{{.body}}
'`, - Engine: mailgun.TemplateEngineGo, + Engine: mtypes.TemplateEngineGo, Tag: "v1", }, }) @@ -970,13 +971,13 @@ func UpdateTemplate(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.UpdateTemplate(ctx, domain, &mailgun.Template{ + return mg.UpdateTemplate(ctx, domain, &mtypes.Template{ Name: "my-template", Description: "Add a description to the template", }) } -func GetTemplate(domain, apiKey string) (mailgun.Template, error) { +func GetTemplate(domain, apiKey string) (mtypes.Template, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -985,14 +986,14 @@ func GetTemplate(domain, apiKey string) (mailgun.Template, error) { return mg.GetTemplate(ctx, domain, "my-template") } -func ListActiveTemplates(domain, apiKey string) ([]mailgun.Template, error) { +func ListActiveTemplates(domain, apiKey string) ([]mtypes.Template, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListTemplates(domain, &mailgun.ListTemplateOptions{Active: true}) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Template + var page, result []mtypes.Template for it.Next(ctx, &page) { result = append(result, page...) } @@ -1003,14 +1004,14 @@ func ListActiveTemplates(domain, apiKey string) ([]mailgun.Template, error) { return result, nil } -func ListTemplates(domain, apiKey string) ([]mailgun.Template, error) { +func ListTemplates(domain, apiKey string) ([]mtypes.Template, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListTemplates(domain, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.Template + var page, result []mtypes.Template for it.Next(ctx, &page) { result = append(result, page...) } @@ -1027,9 +1028,9 @@ func AddTemplateVersion(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.AddTemplateVersion(ctx, domain, "my-template", &mailgun.TemplateVersion{ + return mg.AddTemplateVersion(ctx, domain, "my-template", &mtypes.TemplateVersion{ Template: `'

{{.title}}

{{.body}}
'`, - Engine: mailgun.TemplateEngineGo, + Engine: mtypes.TemplateEngineGo, Tag: "v2", Active: true, }) @@ -1045,7 +1046,7 @@ func DeleteTemplateVersion(domain, apiKey string) error { return mg.DeleteTemplateVersion(ctx, domain, "my-template", "v2") } -func GetTemplateVersion(domain, apiKey string) (mailgun.TemplateVersion, error) { +func GetTemplateVersion(domain, apiKey string) (mtypes.TemplateVersion, error) { mg := mailgun.NewMailgun(apiKey) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) @@ -1061,21 +1062,21 @@ func UpdateTemplateVersion(domain, apiKey string) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - return mg.UpdateTemplateVersion(ctx, domain, "my-template", &mailgun.TemplateVersion{ + return mg.UpdateTemplateVersion(ctx, domain, "my-template", &mtypes.TemplateVersion{ Comment: "Add a comment to the template and make it 'active'", Tag: "v2", Active: true, }) } -func ListTemplateVersions(domain, apiKey string) ([]mailgun.TemplateVersion, error) { +func ListTemplateVersions(domain, apiKey string) ([]mtypes.TemplateVersion, error) { mg := mailgun.NewMailgun(apiKey) it := mg.ListTemplateVersions(domain, "my-template", nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - var page, result []mailgun.TemplateVersion + var page, result []mtypes.TemplateVersion for it.Next(ctx, &page) { result = append(result, page...) } diff --git a/examples_test.go b/examples_test.go index 1e296ab5..02d5ce27 100644 --- a/examples_test.go +++ b/examples_test.go @@ -13,6 +13,7 @@ import ( "github.com/mailgun/mailgun-go/v4" "github.com/mailgun/mailgun-go/v4/events" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func ExampleMailgunImpl_ValidateEmail() { @@ -37,7 +38,7 @@ func ExampleMailgunImpl_UpdateMailingList() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() - _, err := mg.UpdateMailingList(ctx, "joe-stat@example.com", mailgun.MailingList{ + _, err := mg.UpdateMailingList(ctx, "joe-stat@example.com", mtypes.MailingList{ Name: "Joe Stat", Description: "Joe's status report list", }) @@ -99,7 +100,7 @@ func ExampleMailgunImpl_ListRoutes() { defer cancel() it := mg.ListRoutes(nil) - var page []mailgun.Route + var page []mtypes.Route for it.Next(ctx, &page) { for _, r := range page { log.Printf("Route pri=%d expr=%s desc=%s", r.Priority, r.Expression, r.Description) @@ -119,7 +120,7 @@ func ExampleMailgunImpl_VerifyWebhookSignature() { } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - var payload mailgun.WebhookPayload + var payload mtypes.WebhookPayload if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { fmt.Printf("decode JSON error: %s", err) w.WriteHeader(http.StatusNotAcceptable) diff --git a/exports.go b/exports.go index c2223749..88d3e788 100644 --- a/exports.go +++ b/exports.go @@ -5,19 +5,11 @@ import ( "errors" "fmt" "net/http" -) - -type ExportList struct { - Items []Export `json:"items"` -} -type Export struct { - ID string `json:"id"` - Status string `json:"status"` - URL string `json:"url"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) -// Create an export based on the URL given +// CreateExport creates an export based on the URL given func (mg *MailgunImpl) CreateExport(ctx context.Context, url string) error { r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint)) r.setClient(mg.HTTPClient()) @@ -29,8 +21,8 @@ func (mg *MailgunImpl) CreateExport(ctx context.Context, url string) error { return err } -// List all exports created within the past 24 hours -func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]Export, error) { +// ListExports lists all exports created within the past 24 hours +func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]mtypes.Export, error) { r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint)) r.setClient(mg.HTTPClient()) if url != "" { @@ -38,24 +30,24 @@ func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]Export, e } r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp ExportList + var resp mtypes.ExportList if err := getResponseFromJSON(ctx, r, &resp); err != nil { return nil, err } - var result []Export + var result []mtypes.Export for _, item := range resp.Items { - result = append(result, Export(item)) + result = append(result, mtypes.Export(item)) } return result, nil } // GetExport gets an export by id -func (mg *MailgunImpl) GetExport(ctx context.Context, id string) (Export, error) { +func (mg *MailgunImpl) GetExport(ctx context.Context, id string) (mtypes.Export, error) { r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint) + "/" + id) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp Export + var resp mtypes.Export err := getResponseFromJSON(ctx, r, &resp) return resp, err } diff --git a/httphelpers.go b/http.go similarity index 100% rename from httphelpers.go rename to http.go diff --git a/integration_test.go b/integration_test.go index ca81f4eb..c013b7c5 100644 --- a/integration_test.go +++ b/integration_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,18 +25,18 @@ func TestIntegrationMailgunImpl_ListMetrics(t *testing.T) { domain := os.Getenv("MG_DOMAIN") require.NotEmpty(t, domain) - opts := mailgun.MetricsOptions{ - End: mailgun.RFC2822Time(time.Now().UTC()), + opts := mtypes.MetricsRequest{ + End: mtypes.RFC2822Time(time.Now().UTC()), Duration: "30d", - Pagination: mailgun.MetricsPagination{ + Pagination: mtypes.MetricsPagination{ Limit: 10, }, } // filter by domain - opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{ + opts.Filter.BoolGroupAnd = []mtypes.MetricsFilterPredicate{{ Attribute: "domain", Comparator: "=", - LabeledValues: []mailgun.MetricsLabeledValue{{Label: domain, Value: domain}}, + LabeledValues: []mtypes.MetricsLabeledValue{{Label: domain, Value: domain}}, }} iter, err := mg.ListMetrics(opts) @@ -46,7 +47,7 @@ func TestIntegrationMailgunImpl_ListMetrics(t *testing.T) { defer cancel() for i := 0; i < 2; i++ { - var resp mailgun.MetricsResponse + var resp mtypes.MetricsResponse more := iter.Next(ctx, &resp) if iter.Err() != nil { require.NoError(t, err) diff --git a/ips.go b/ips.go index 7a944571..901f6e89 100644 --- a/ips.go +++ b/ips.go @@ -1,25 +1,13 @@ package mailgun -import "context" +import ( + "context" -type ipAddressListResponse struct { - TotalCount int `json:"total_count"` - Items []string `json:"items"` -} - -type IPAddress struct { - IP string `json:"ip"` - RDNS string `json:"rdns"` - Dedicated bool `json:"dedicated"` -} - -type okResp struct { - ID string `json:"id,omitempty"` - Message string `json:"message"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // ListIPS returns a list of IPs assigned to your account -func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error) { +func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]mtypes.IPAddress, error) { r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint)) r.setClient(mg.HTTPClient()) if dedicated { @@ -27,40 +15,40 @@ func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]IPAddress } r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp ipAddressListResponse + var resp mtypes.IPAddressListResponse if err := getResponseFromJSON(ctx, r, &resp); err != nil { return nil, err } - var result []IPAddress + var result []mtypes.IPAddress for _, ip := range resp.Items { - result = append(result, IPAddress{IP: ip}) + result = append(result, mtypes.IPAddress{IP: ip}) } return result, nil } // GetIP returns information about the specified IP -func (mg *MailgunImpl) GetIP(ctx context.Context, ip string) (IPAddress, error) { +func (mg *MailgunImpl) GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error) { r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint) + "/" + ip) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp IPAddress + var resp mtypes.IPAddress err := getResponseFromJSON(ctx, r, &resp) return resp, err } // ListDomainIPS returns a list of IPs currently assigned to the specified domain. -func (mg *MailgunImpl) ListDomainIPS(ctx context.Context, domain string) ([]IPAddress, error) { +func (mg *MailgunImpl) ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IPAddress, error) { r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/ips") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp ipAddressListResponse + var resp mtypes.IPAddressListResponse if err := getResponseFromJSON(ctx, r, &resp); err != nil { return nil, err } - var result []IPAddress + var result []mtypes.IPAddress for _, ip := range resp.Items { - result = append(result, IPAddress{IP: ip}) + result = append(result, mtypes.IPAddress{IP: ip}) } return result, nil } diff --git a/limits.go b/limits.go index 2e73eadf..bcb30c77 100644 --- a/limits.go +++ b/limits.go @@ -1,18 +1,17 @@ package mailgun -import "context" +import ( + "context" -type TagLimits struct { - Limit int `json:"limit"` - Count int `json:"count"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // GetTagLimits returns tracking settings for a domain -func (mg *MailgunImpl) GetTagLimits(ctx context.Context, domain string) (TagLimits, error) { +func (mg *MailgunImpl) GetTagLimits(ctx context.Context, domain string) (mtypes.TagLimits, error) { r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/limits/tag") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp TagLimits + var resp mtypes.TagLimits err := getResponseFromJSON(ctx, r, &resp) return resp, err } diff --git a/mailgun.go b/mailgun.go index edb39859..d8518517 100644 --- a/mailgun.go +++ b/mailgun.go @@ -80,7 +80,10 @@ import ( "fmt" "net/http" "os" + "strconv" "time" + + "github.com/mailgun/mailgun-go/v4/mtypes" ) // Debug set true to write the HTTP requests in curl for to stdout @@ -134,35 +137,35 @@ type Mailgun interface { ReSend(ctx context.Context, id string, recipients ...string) (string, string, error) ListBounces(domain string, opts *ListOptions) *BouncesIterator - GetBounce(ctx context.Context, domain, address string) (Bounce, error) + GetBounce(ctx context.Context, domain, address string) (mtypes.Bounce, error) AddBounce(ctx context.Context, domain, address, code, err string) error DeleteBounce(ctx context.Context, domain, address string) error DeleteBounceList(ctx context.Context, domain string) error ListMetrics(opts MetricsOptions) (*MetricsIterator, error) - GetTag(ctx context.Context, domain, tag string) (Tag, error) + GetTag(ctx context.Context, domain, tag string) (mtypes.Tag, error) DeleteTag(ctx context.Context, domain, tag string) error ListTags(domain string, opts *ListTagOptions) *TagIterator ListDomains(opts *ListDomainsOptions) *DomainsIterator - GetDomain(ctx context.Context, domain string, opts *GetDomainOptions) (GetDomainResponse, error) - CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (GetDomainResponse, error) + GetDomain(ctx context.Context, domain string, opts *GetDomainOptions) (mtypes.GetDomainResponse, error) + CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (mtypes.GetDomainResponse, error) DeleteDomain(ctx context.Context, name string) error - VerifyDomain(ctx context.Context, name string) (GetDomainResponse, error) + VerifyDomain(ctx context.Context, name string) (mtypes.GetDomainResponse, error) - UpdateDomainConnection(ctx context.Context, domain string, dc DomainConnection) error - GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error) + UpdateDomainConnection(ctx context.Context, domain string, dc mtypes.DomainConnection) error + GetDomainConnection(ctx context.Context, domain string) (mtypes.DomainConnection, error) - GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error) + GetDomainTracking(ctx context.Context, domain string) (mtypes.DomainTracking, error) UpdateClickTracking(ctx context.Context, domain, active string) error UpdateUnsubscribeTracking(ctx context.Context, domain, active, htmlFooter, textFooter string) error UpdateOpenTracking(ctx context.Context, domain, active string) error UpdateDomainDkimSelector(ctx context.Context, domain, dkimSelector string) error - GetStoredMessage(ctx context.Context, url string) (StoredMessage, error) - GetStoredMessageRaw(ctx context.Context, id string) (StoredMessageRaw, error) + GetStoredMessage(ctx context.Context, url string) (mtypes.StoredMessage, error) + GetStoredMessageRaw(ctx context.Context, id string) (mtypes.StoredMessageRaw, error) GetStoredAttachment(ctx context.Context, url string) ([]byte, error) ListCredentials(domain string, opts *ListOptions) *CredentialsIterator @@ -171,81 +174,81 @@ type Mailgun interface { DeleteCredential(ctx context.Context, domain, login string) error ListUnsubscribes(domain string, opts *ListOptions) *UnsubscribesIterator - GetUnsubscribe(ctx context.Context, domain, address string) (Unsubscribe, error) + GetUnsubscribe(ctx context.Context, domain, address string) (mtypes.Unsubscribe, error) CreateUnsubscribe(ctx context.Context, domain, address, tag string) error - CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []Unsubscribe) error + CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []mtypes.Unsubscribe) error DeleteUnsubscribe(ctx context.Context, domain, address string) error DeleteUnsubscribeWithTag(ctx context.Context, domain, a, t string) error ListComplaints(domain string, opts *ListOptions) *ComplaintsIterator - GetComplaint(ctx context.Context, domain, address string) (Complaint, error) + GetComplaint(ctx context.Context, domain, address string) (mtypes.Complaint, error) CreateComplaint(ctx context.Context, domain, address string) error CreateComplaints(ctx context.Context, domain string, addresses []string) error DeleteComplaint(ctx context.Context, domain, address string) error ListRoutes(opts *ListOptions) *RoutesIterator - GetRoute(ctx context.Context, address string) (Route, error) - CreateRoute(ctx context.Context, address Route) (Route, error) + GetRoute(ctx context.Context, address string) (mtypes.Route, error) + CreateRoute(ctx context.Context, address mtypes.Route) (mtypes.Route, error) DeleteRoute(ctx context.Context, address string) error - UpdateRoute(ctx context.Context, address string, r Route) (Route, error) + UpdateRoute(ctx context.Context, address string, r mtypes.Route) (mtypes.Route, error) ListWebhooks(ctx context.Context, domain string) (map[string][]string, error) CreateWebhook(ctx context.Context, domain, kind string, url []string) error DeleteWebhook(ctx context.Context, domain, kind string) error GetWebhook(ctx context.Context, domain, kind string) ([]string, error) UpdateWebhook(ctx context.Context, domain, kind string, url []string) error - VerifyWebhookSignature(sig Signature) (verified bool, err error) + VerifyWebhookSignature(sig mtypes.Signature) (verified bool, err error) ListMailingLists(opts *ListOptions) *ListsIterator - CreateMailingList(ctx context.Context, address MailingList) (MailingList, error) + CreateMailingList(ctx context.Context, address mtypes.MailingList) (mtypes.MailingList, error) DeleteMailingList(ctx context.Context, address string) error - GetMailingList(ctx context.Context, address string) (MailingList, error) - UpdateMailingList(ctx context.Context, address string, ml MailingList) (MailingList, error) + GetMailingList(ctx context.Context, address string) (mtypes.MailingList, error) + UpdateMailingList(ctx context.Context, address string, ml mtypes.MailingList) (mtypes.MailingList, error) ListMembers(address string, opts *ListOptions) *MemberListIterator - GetMember(ctx context.Context, MemberAddr, listAddr string) (Member, error) - CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error + GetMember(ctx context.Context, MemberAddr, listAddr string) (mtypes.Member, error) + CreateMember(ctx context.Context, merge bool, addr string, prototype mtypes.Member) error CreateMemberList(ctx context.Context, subscribed *bool, addr string, newMembers []any) error - UpdateMember(ctx context.Context, Member, list string, prototype Member) (Member, error) + UpdateMember(ctx context.Context, Member, list string, prototype mtypes.Member) (mtypes.Member, error) DeleteMember(ctx context.Context, Member, list string) error ListEvents(domain string, opts *ListEventOptions) *EventIterator PollEvents(domain string, opts *ListEventOptions) *EventPoller - ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error) - GetIP(ctx context.Context, ip string) (IPAddress, error) - ListDomainIPS(ctx context.Context, domain string) ([]IPAddress, error) + ListIPS(ctx context.Context, dedicated bool) ([]mtypes.IPAddress, error) + GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error) + ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IPAddress, error) AddDomainIP(ctx context.Context, domain, ip string) error DeleteDomainIP(ctx context.Context, domain, ip string) error - ListExports(ctx context.Context, url string) ([]Export, error) - GetExport(ctx context.Context, id string) (Export, error) + ListExports(ctx context.Context, url string) ([]mtypes.Export, error) + GetExport(ctx context.Context, id string) (mtypes.Export, error) GetExportLink(ctx context.Context, id string) (string, error) CreateExport(ctx context.Context, url string) error - GetTagLimits(ctx context.Context, domain string) (TagLimits, error) + GetTagLimits(ctx context.Context, domain string) (mtypes.TagLimits, error) - CreateTemplate(ctx context.Context, domain string, template *Template) error - GetTemplate(ctx context.Context, domain, name string) (Template, error) - UpdateTemplate(ctx context.Context, domain string, template *Template) error + CreateTemplate(ctx context.Context, domain string, template *mtypes.Template) error + GetTemplate(ctx context.Context, domain, name string) (mtypes.Template, error) + UpdateTemplate(ctx context.Context, domain string, template *mtypes.Template) error DeleteTemplate(ctx context.Context, domain, name string) error ListTemplates(domain string, opts *ListTemplateOptions) *TemplatesIterator - AddTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error - GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (TemplateVersion, error) - UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error + AddTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error + GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (mtypes.TemplateVersion, error) + UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error DeleteTemplateVersion(ctx context.Context, domain, templateName, tag string) error ListTemplateVersions(domain, templateName string, opts *ListOptions) *TemplateVersionsIterator - ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (ValidateEmailResponse, error) + ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (mtypes.ValidateEmailResponse, error) ListSubaccounts(opts *ListSubaccountsOptions) *SubaccountsIterator - CreateSubaccount(ctx context.Context, subaccountName string) (SubaccountResponse, error) - GetSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) - EnableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) - DisableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) + CreateSubaccount(ctx context.Context, subaccountName string) (mtypes.SubaccountResponse, error) + GetSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error) + EnableSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error) + DisableSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error) - SetOnBehalfOfSubaccount(subaccountId string) + SetOnBehalfOfSubaccount(subaccountID string) RemoveOnBehalfOfSubaccount() } @@ -369,6 +372,11 @@ type ListOptions struct { Limit int } +// TimeToFloat given time.Time{} return a float64 as given in mailgun event timestamps +func TimeToFloat(t time.Time) float64 { + return float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000)) +} + // TODO(vtopc): sort all these generate URL functions(some are generateApi...Url, other are generate...ApiUrl) func generateApiUrlWithDomain(m Mailgun, version int, endpoint, domain string) string { @@ -427,3 +435,8 @@ func formatMailgunTime(t time.Time) string { func ptr[T any](v T) *T { return &v } + +// TODO(vtopc): remove boolToString and use strconv.FormatBool() directly. +func boolToString(b bool) string { + return strconv.FormatBool(b) +} diff --git a/mailgun_test.go b/mailgun_test.go index c47541ca..a38d836a 100644 --- a/mailgun_test.go +++ b/mailgun_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -33,7 +34,7 @@ func TestInvalidBaseAPI(t *testing.T) { func TestValidBaseAPI(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - var resp mailgun.GetDomainResponse + var resp mtypes.GetDomainResponse b, err := json.Marshal(resp) require.NoError(t, err) diff --git a/mailing_lists.go b/mailing_lists.go index a1f36137..10a7aee3 100644 --- a/mailing_lists.go +++ b/mailing_lists.go @@ -3,60 +3,12 @@ package mailgun import ( "context" "strconv" -) - -// A mailing list may have one of three membership modes. -const ( - // ReadOnly specifies that nobody, including Members, may send messages to - // the mailing list. Messages distributed on such lists come from list - // administrator accounts only. - AccessLevelReadOnly = "readonly" - // Members specifies that only those who subscribe to the mailing list may - // send messages. - AccessLevelMembers = "members" - // Everyone specifies that anyone and everyone may both read and submit - // messages to the mailing list, including non-subscribers. - AccessLevelEveryone = "everyone" -) -// Specify the access of a mailing list member -type AccessLevel string - -// Replies to a mailing list should go to one of two preferred destinations. -const ( - // List specifies that replies should be sent to the mailing list address. - ReplyPreferenceList = "list" - // Sender specifies that replies should be sent to the sender (FROM) address. - ReplyPreferenceSender = "sender" + "github.com/mailgun/mailgun-go/v4/mtypes" ) -// Set where replies should go -type ReplyPreference string - -// A List structure provides information for a mailing list. -// -// AccessLevel may be one of ReadOnly, Members, or Everyone. -type MailingList struct { - Address string `json:"address,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - AccessLevel AccessLevel `json:"access_level,omitempty"` - ReplyPreference ReplyPreference `json:"reply_preference,omitempty"` - CreatedAt RFC2822Time `json:"created_at,omitempty"` - MembersCount int `json:"members_count,omitempty"` -} - -type listsResponse struct { - Items []MailingList `json:"items"` - Paging Paging `json:"paging"` -} - -type mailingListResponse struct { - MailingList MailingList `json:"list"` -} - type ListsIterator struct { - listsResponse + mtypes.ListMailingListsResponse mg Mailgun err error } @@ -73,9 +25,9 @@ func (mg *MailgunImpl) ListMailingLists(opts *ListOptions) *ListsIterator { } url, err := r.generateUrlWithParameters() return &ListsIterator{ - mg: mg, - listsResponse: listsResponse{Paging: Paging{Next: url, First: url}}, - err: err, + mg: mg, + ListMailingListsResponse: mtypes.ListMailingListsResponse{Paging: mtypes.Paging{Next: url, First: url}}, + err: err, } } @@ -87,7 +39,7 @@ func (li *ListsIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool { +func (li *ListsIterator) Next(ctx context.Context, items *[]mtypes.MailingList) bool { if li.err != nil { return false } @@ -95,7 +47,7 @@ func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool { if li.err != nil { return false } - cpy := make([]MailingList, len(li.Items)) + cpy := make([]mtypes.MailingList, len(li.Items)) copy(cpy, li.Items) *items = cpy @@ -105,7 +57,7 @@ func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool { +func (li *ListsIterator) First(ctx context.Context, items *[]mtypes.MailingList) bool { if li.err != nil { return false } @@ -113,7 +65,7 @@ func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool { if li.err != nil { return false } - cpy := make([]MailingList, len(li.Items)) + cpy := make([]mtypes.MailingList, len(li.Items)) copy(cpy, li.Items) *items = cpy return true @@ -123,7 +75,7 @@ func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool { // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool { +func (li *ListsIterator) Last(ctx context.Context, items *[]mtypes.MailingList) bool { if li.err != nil { return false } @@ -131,7 +83,7 @@ func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool { if li.err != nil { return false } - cpy := make([]MailingList, len(li.Items)) + cpy := make([]mtypes.MailingList, len(li.Items)) copy(cpy, li.Items) *items = cpy return true @@ -140,7 +92,7 @@ func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) bool { +func (li *ListsIterator) Previous(ctx context.Context, items *[]mtypes.MailingList) bool { if li.err != nil { return false } @@ -151,7 +103,7 @@ func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) boo if li.err != nil { return false } - cpy := make([]MailingList, len(li.Items)) + cpy := make([]mtypes.MailingList, len(li.Items)) copy(cpy, li.Items) *items = cpy @@ -164,7 +116,7 @@ func (li *ListsIterator) fetch(ctx context.Context, url string) error { r.setClient(li.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, li.mg.APIKey()) - return getResponseFromJSON(ctx, r, &li.listsResponse) + return getResponseFromJSON(ctx, r, &li.ListMailingListsResponse) } // CreateMailingList creates a new mailing list under your Mailgun account. @@ -173,7 +125,7 @@ func (li *ListsIterator) fetch(ctx context.Context, url string) error { // If unspecified, Description remains blank, // while AccessLevel defaults to Everyone // and ReplyPreference defaults to List. -func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype MailingList) (MailingList, error) { +func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype mtypes.MailingList) (mtypes.MailingList, error) { r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -195,9 +147,9 @@ func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype MailingL } response, err := makePostRequest(ctx, r, p) if err != nil { - return MailingList{}, err + return mtypes.MailingList{}, err } - var l MailingList + var l mtypes.MailingList err = response.parseFromJSON(&l) return l, err } @@ -214,16 +166,16 @@ func (mg *MailgunImpl) DeleteMailingList(ctx context.Context, addr string) error // GetMailingList allows your application to recover the complete List structure // representing a mailing list, so long as you have its e-mail address. -func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (MailingList, error) { +func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (mtypes.MailingList, error) { r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint) + "/" + addr) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) response, err := makeGetRequest(ctx, r) if err != nil { - return MailingList{}, err + return mtypes.MailingList{}, err } - var resp mailingListResponse + var resp mtypes.GetMailingListResponse err = response.parseFromJSON(&resp) return resp.MailingList, err } @@ -235,7 +187,7 @@ func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (Mailing // Be careful! If changing the address of a mailing list, // e-mail sent to the old address will not succeed. // Make sure you account for the change accordingly. -func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, prototype MailingList) (MailingList, error) { +func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, prototype mtypes.MailingList) (mtypes.MailingList, error) { r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint) + "/" + addr) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -255,7 +207,7 @@ func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, proto if prototype.ReplyPreference != "" { p.addValue("reply_preference", string(prototype.ReplyPreference)) } - var l MailingList + var l mtypes.MailingList response, err := makePutRequest(ctx, r, p) if err != nil { return l, err diff --git a/mailing_lists_test.go b/mailing_lists_test.go index 03aead83..ae07a301 100644 --- a/mailing_lists_test.go +++ b/mailing_lists_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -17,11 +18,11 @@ func TestMailingListMembers(t *testing.T) { ctx := context.Background() address := randomEmail("list", testDomain) - _, err = mg.CreateMailingList(ctx, mailgun.MailingList{ + _, err = mg.CreateMailingList(ctx, mtypes.MailingList{ Address: address, Name: address, Description: "TestMailingListMembers-related mailing list", - AccessLevel: mailgun.AccessLevelMembers, + AccessLevel: mtypes.AccessLevelMembers, }) require.NoError(t, err) defer func() { @@ -29,7 +30,7 @@ func TestMailingListMembers(t *testing.T) { }() var countMembers = func() int { - var page []mailgun.Member + var page []mtypes.Member var count int it := mg.ListMembers(address, nil) @@ -41,10 +42,10 @@ func TestMailingListMembers(t *testing.T) { } startCount := countMembers() - protoJoe := mailgun.Member{ + protoJoe := mtypes.Member{ Address: "joe@example.com", Name: "Joe Example", - Subscribed: mailgun.Subscribed, + Subscribed: mtypes.Subscribed, } require.NoError(t, mg.CreateMember(ctx, true, address, protoJoe)) newCount := countMembers() @@ -57,7 +58,7 @@ func TestMailingListMembers(t *testing.T) { assert.Equal(t, protoJoe.Subscribed, theMember.Subscribed) assert.Len(t, theMember.Vars, 0) - _, err = mg.UpdateMember(ctx, "joe@example.com", address, mailgun.Member{ + _, err = mg.UpdateMember(ctx, "joe@example.com", address, mtypes.Member{ Name: "Joe Cool", }) require.NoError(t, err) @@ -69,17 +70,17 @@ func TestMailingListMembers(t *testing.T) { assert.Equal(t, startCount, countMembers()) err = mg.CreateMemberList(ctx, nil, address, []any{ - mailgun.Member{ + mtypes.Member{ Address: "joe.user1@example.com", Name: "Joe's debugging account", - Subscribed: mailgun.Unsubscribed, + Subscribed: mtypes.Unsubscribed, }, - mailgun.Member{ + mtypes.Member{ Address: "Joe Cool ", Name: "Joe's Cool Account", - Subscribed: mailgun.Subscribed, + Subscribed: mtypes.Subscribed, }, - mailgun.Member{ + mtypes.Member{ Address: "joe.user3@example.com", Vars: map[string]any{ "packet-email": "KW9ABC @ BOGBBS-4.#NCA.CA.USA.NOAM", @@ -103,18 +104,18 @@ func TestMailingLists(t *testing.T) { ctx := context.Background() address := randomEmail("list", testDomain) - protoList := mailgun.MailingList{ + protoList := mtypes.MailingList{ Address: address, Name: "List1", Description: "A list created by an acceptance test.", - AccessLevel: mailgun.AccessLevelMembers, - ReplyPreference: mailgun.ReplyPreferenceSender, + AccessLevel: mtypes.AccessLevelMembers, + ReplyPreference: mtypes.ReplyPreferenceSender, } var countLists = func() int { var count int it := mg.ListMailingLists(nil) - var page []mailgun.MailingList + var page []mtypes.MailingList for it.Next(ctx, &page) { count += len(page) } @@ -140,7 +141,7 @@ func TestMailingLists(t *testing.T) { protoList.CreatedAt = theList.CreatedAt // ignore this field when comparing. assert.Equal(t, theList, protoList) - _, err = mg.UpdateMailingList(ctx, address, mailgun.MailingList{ + _, err = mg.UpdateMailingList(ctx, address, mtypes.MailingList{ Description: "A list whose description changed", }) require.NoError(t, err) @@ -161,7 +162,7 @@ func TestListMailingListRegression(t *testing.T) { ctx := context.Background() address := "test@example.com" - _, err = mg.CreateMailingList(ctx, mailgun.MailingList{ + _, err = mg.CreateMailingList(ctx, mtypes.MailingList{ Address: address, Name: "paging", Description: "Test paging", @@ -174,7 +175,7 @@ func TestListMailingListRegression(t *testing.T) { vars = map[string]any{"has": "vars"} } - err := mg.CreateMember(ctx, false, address, mailgun.Member{ + err := mg.CreateMember(ctx, false, address, mtypes.Member{ Address: fmt.Sprintf("%03d@example.com", i), Vars: vars, }) @@ -183,7 +184,7 @@ func TestListMailingListRegression(t *testing.T) { it := mg.ListMembers(address, nil) - var members []mailgun.Member + var members []mtypes.Member var found int for it.Next(ctx, &members) { for _, m := range members { diff --git a/members.go b/members.go index 2b483547..747fb416 100644 --- a/members.go +++ b/members.go @@ -4,48 +4,12 @@ import ( "context" "encoding/json" "strconv" -) - -// yes and no are variables which provide us the ability to take their addresses. -// Subscribed and Unsubscribed are pointers to these booleans. -// -// We use a pointer to boolean as a kind of trinary data type: -// if nil, the relevant data type remains unspecified. -// Otherwise, its value is either true or false. -var ( - yes = true - no = false -) -// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not. -// This attribute may be used to filter the results returned by GetSubscribers(). -// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search. -var ( - All *bool - Subscribed = &yes - Unsubscribed = &no + "github.com/mailgun/mailgun-go/v4/mtypes" ) -// A Member structure represents a member of the mailing list. -// The Vars field can represent any JSON-encodable data. -type Member struct { - Address string `json:"address,omitempty"` - Name string `json:"name,omitempty"` - Subscribed *bool `json:"subscribed,omitempty"` - Vars map[string]any `json:"vars,omitempty"` -} - -type memberListResponse struct { - Lists []Member `json:"items"` - Paging Paging `json:"paging"` -} - -type memberResponse struct { - Member Member `json:"member"` -} - type MemberListIterator struct { - memberListResponse + mtypes.MemberListResponse mg Mailgun err error } @@ -62,7 +26,7 @@ func (mg *MailgunImpl) ListMembers(address string, opts *ListOptions) *MemberLis url, err := r.generateUrlWithParameters() return &MemberListIterator{ mg: mg, - memberListResponse: memberListResponse{Paging: Paging{Next: url, First: url}}, + MemberListResponse: mtypes.MemberListResponse{Paging: mtypes.Paging{Next: url, First: url}}, err: err, } } @@ -75,7 +39,7 @@ func (li *MemberListIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool { +func (li *MemberListIterator) Next(ctx context.Context, items *[]mtypes.Member) bool { if li.err != nil { return false } @@ -91,7 +55,7 @@ func (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool { +func (li *MemberListIterator) First(ctx context.Context, items *[]mtypes.Member) bool { if li.err != nil { return false } @@ -107,7 +71,7 @@ func (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool { // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool { +func (li *MemberListIterator) Last(ctx context.Context, items *[]mtypes.Member) bool { if li.err != nil { return false } @@ -122,7 +86,7 @@ func (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (li *MemberListIterator) Previous(ctx context.Context, items *[]Member) bool { +func (li *MemberListIterator) Previous(ctx context.Context, items *[]mtypes.Member) bool { if li.err != nil { return false } @@ -144,20 +108,20 @@ func (li *MemberListIterator) fetch(ctx context.Context, url string) error { r.setClient(li.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, li.mg.APIKey()) - return getResponseFromJSON(ctx, r, &li.memberListResponse) + return getResponseFromJSON(ctx, r, &li.MemberListResponse) } // GetMember returns a complete Member structure for a member of a mailing list, // given only their subscription e-mail address. -func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, error) { +func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (mtypes.Member, error) { r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) response, err := makeGetRequest(ctx, r) if err != nil { - return Member{}, err + return mtypes.Member{}, err } - var resp memberResponse + var resp mtypes.MemberResponse err = response.parseFromJSON(&resp) return resp.Member, err } @@ -165,7 +129,7 @@ func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, erro // CreateMember registers a new member of the indicated mailing list. // If merge is set to true, then the registration may update an existing Member's settings. // Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address. -func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error { +func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype mtypes.Member) error { vs, err := json.Marshal(prototype.Vars) if err != nil { return err @@ -188,7 +152,7 @@ func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string // UpdateMember lets you change certain details about the indicated mailing list member. // Address, Name, Vars, and Subscribed fields may be changed. -func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype Member) (Member, error) { +func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype mtypes.Member) (mtypes.Member, error) { r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -202,7 +166,7 @@ func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype if prototype.Vars != nil { vs, err := json.Marshal(prototype.Vars) if err != nil { - return Member{}, err + return mtypes.Member{}, err } p.addValue("vars", string(vs)) } @@ -211,10 +175,10 @@ func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype } response, err := makePutRequest(ctx, r, p) if err != nil { - return Member{}, err + return mtypes.Member{}, err } var envelope struct { - Member Member `json:"member"` + Member mtypes.Member `json:"member"` } err = response.parseFromJSON(&envelope) return envelope.Member, err diff --git a/messages.go b/messages.go index a6ace90f..8efbb0e3 100644 --- a/messages.go +++ b/messages.go @@ -114,9 +114,10 @@ type MimeMessage struct { } // TODO(v5): return from Send() +// TODO(v5): move to mtypes? type sendMessageResponse struct { Message string `json:"message"` - Id string `json:"id"` + ID string `json:"id"` } // TrackingOptions contains fields relevant to tracking. @@ -696,7 +697,7 @@ func (mg *MailgunImpl) Send(ctx context.Context, m SendableMessage) (mes, id str err = postResponseFromJSON(ctx, r, payload, &response) if err == nil { mes = response.Message - id = response.Id + id = response.ID } return mes, id, err diff --git a/messages_types_v5.go b/messages_types_v5.go deleted file mode 100644 index 92139081..00000000 --- a/messages_types_v5.go +++ /dev/null @@ -1,64 +0,0 @@ -package mailgun - -// This file contains a draft for new v5 messages. -// TODO(v5): remove this file - -import ( - "io" - "time" -) - -// CommonMessage structures contain both the message text and the envelope for an e-mail message. -// TODO(v5): rename to CommonMessage -type commonMessageV5 struct { - domain string - to []string - tags []string - dkim *bool - deliveryTime time.Time - stoPeriod string - attachments []string - readerAttachments []ReaderAttachment - inlines []string - readerInlines []ReaderAttachment - bufferAttachments []BufferAttachment - nativeSend bool - testMode bool - tracking *bool - trackingClicks *string - trackingOpens *bool - headers map[string]string - variables map[string]string - templateVariables map[string]any - recipientVariables map[string]map[string]any - templateVersionTag string - templateRenderText bool - requireTLS bool - skipVerification bool -} - -// PlainMessage contains fields relevant to plain API-synthesized messages. -// You're expected to use various setters to set most of these attributes, -// although from, subject, and text are set when the message is created with -// NewMessage. -// TODO(v5): rename to PlainMessage -type plainMessageV5 struct { - commonMessageV5 - - from string - cc []string - bcc []string - subject string - text string - html string - ampHtml string - template string -} - -// MimeMessage contains fields relevant to pre-packaged MIME messages. -// TODO(v5): rename to MimeMessage -type mimeMessageV5 struct { - commonMessageV5 - - body io.ReadCloser -} diff --git a/messages_v5.go b/messages_v5.go deleted file mode 100644 index 4a7c313a..00000000 --- a/messages_v5.go +++ /dev/null @@ -1,488 +0,0 @@ -package mailgun - -// This file contains methods for new v5 messages. -// TODO(v5): remove this file - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "regexp" - "strconv" - "strings" - "time" -) - -// NewMessage returns a new e-mail message with the simplest envelop needed to send. -// -// Supports arbitrary-sized recipient lists by -// automatically sending mail in batches of up to MaxNumberOfRecipients. -// -// To support batch sending, do not provide `to` at this point. -// You can do this explicitly, or implicitly, as follows: -// -// // Note absence of `to` parameter(s)! -// m := NewMessage("me@example.com", "Help save our planet", "Hello world!") -// -// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method -// before sending, though. -// TODO(v5): rename to NewMessage -func newMessageV5(domain, from, subject, text string, to ...string) *plainMessageV5 { - return &plainMessageV5{ - commonMessageV5: commonMessageV5{ - domain: domain, - to: to, - }, - - from: from, - subject: subject, - text: text, - } -} - -// NewMIMEMessage creates a new MIME message. These messages are largely canned; -// you do not need to invoke setters to set message-related headers. -// However, you do still need to call setters for Mailgun-specific settings. -// -// Supports arbitrary-sized recipient lists by -// automatically sending mail in batches of up to MaxNumberOfRecipients. -// -// To support batch sending, do not provide `to` at this point. -// You can do this explicitly, or implicitly, as follows: -// -// // Note absence of `to` parameter(s)! -// m := NewMIMEMessage(body) -// -// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method -// before sending, though. -// TODO(v5): rename to NewMIMEMessage -func newMIMEMessage(domain string, body io.ReadCloser, to ...string) *mimeMessageV5 { - return &mimeMessageV5{ - commonMessageV5: commonMessageV5{ - domain: domain, - to: to, - }, - body: body, - } -} - -// AddReaderAttachment arranges to send a file along with the e-mail message. -// File contents are read from a io.ReadCloser. -// The filename parameter is the resulting filename of the attachment. -// The readCloser parameter is the io.ReadCloser which reads the actual bytes to be used -// as the contents of the attached file. -func (m *commonMessageV5) AddReaderAttachment(filename string, readCloser io.ReadCloser) { - ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser} - m.readerAttachments = append(m.readerAttachments, ra) -} - -// AddBufferAttachment arranges to send a file along with the e-mail message. -// File contents are read from the []byte array provided -// The filename parameter is the resulting filename of the attachment. -// The buffer parameter is the []byte array which contains the actual bytes to be used -// as the contents of the attached file. -func (m *commonMessageV5) AddBufferAttachment(filename string, buffer []byte) { - ba := BufferAttachment{Filename: filename, Buffer: buffer} - m.bufferAttachments = append(m.bufferAttachments, ba) -} - -// AddAttachment arranges to send a file from the filesystem along with the e-mail message. -// The attachment parameter is a filename, which must refer to a file which actually resides -// in the local filesystem. -func (m *commonMessageV5) AddAttachment(attachment string) { - m.attachments = append(m.attachments, attachment) -} - -// AddReaderInline arranges to send a file along with the e-mail message. -// File contents are read from a io.ReadCloser. -// The filename parameter is the resulting filename of the attachment. -// The readCloser parameter is the io.ReadCloser which reads the actual bytes to be used -// as the contents of the attached file. -func (m *commonMessageV5) AddReaderInline(filename string, readCloser io.ReadCloser) { - ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser} - m.readerInlines = append(m.readerInlines, ra) -} - -// AddInline arranges to send a file along with the e-mail message, but does so -// in a way that its data remains "inline" with the rest of the message. This -// can be used to send image or font data along with an HTML-encoded message body. -// The attachment parameter is a filename, which must refer to a file which actually resides -// in the local filesystem. -func (m *commonMessageV5) AddInline(inline string) { - m.inlines = append(m.inlines, inline) -} - -// SetDKIM arranges to send the o:dkim header with the message, and sets its value accordingly. -// Refer to the Mailgun documentation for more information. -func (m *commonMessageV5) SetDKIM(dkim bool) { - m.dkim = &dkim -} - -// EnableNativeSend allows the return path to match the address in the CommonMessage.Headers.From: -// field when sending from Mailgun rather than the usual bounce+ address in the return path. -func (m *commonMessageV5) EnableNativeSend() { - m.nativeSend = true -} - -// EnableTestMode allows submittal of a message, such that it will be discarded by Mailgun. -// This facilitates testing client-side software without actually consuming e-mail resources. -func (m *commonMessageV5) EnableTestMode() { - m.testMode = true -} - -// SetDeliveryTime schedules the message for transmission at the indicated time. -// Pass nil to remove any installed schedule. -// Refer to the Mailgun documentation for more information. -func (m *commonMessageV5) SetDeliveryTime(dt time.Time) { - m.deliveryTime = dt -} - -// SetSTOPeriod toggles Send Time Optimization (STO) on a per-message basis. -// String should be set to the number of hours in [0-9]+h format, -// with the minimum being 24h and the maximum being 72h. -// Refer to the Mailgun documentation for more information. -func (m *commonMessageV5) SetSTOPeriod(stoPeriod string) error { - validPattern := `^([2-6][4-9]|[3-6][0-9]|7[0-2])h$` - // TODO(vtopc): regexp.Compile, which is called by regexp.MatchString, is a heave operation, move into global variable - // or just parse using time.ParseDuration(). - match, err := regexp.MatchString(validPattern, stoPeriod) - if err != nil { - return err - } - - if !match { - return errors.New("STO period is invalid. Valid range is 24h to 72h") - } - - m.stoPeriod = stoPeriod - return nil -} - -// SetTracking sets the o:tracking message parameter to adjust, on a message-by-message basis, -// whether or not Mailgun will rewrite URLs to facilitate event tracking. -// Events tracked includes opens, clicks, unsubscribes, etc. -// Note: simply calling this method ensures that the o:tracking header is passed in with the message. -// Its yes/no setting is determined by the call's parameter. -// Note that this header is not passed on to the final recipient(s). -// Refer to the Mailgun documentation for more information. -func (m *commonMessageV5) SetTracking(tracking bool) { - m.tracking = &tracking -} - -// SetTrackingClicks information is found in the Mailgun documentation. -func (m *commonMessageV5) SetTrackingClicks(trackingClicks bool) { - m.trackingClicks = ptr(yesNo(trackingClicks)) -} - -// SetTrackingOptions sets the o:tracking, o:tracking-clicks and o:tracking-opens at once. -func (m *commonMessageV5) SetTrackingOptions(options *TrackingOptions) { - m.tracking = &options.Tracking - - m.trackingClicks = &options.TrackingClicks - - m.trackingOpens = &options.TrackingOpens -} - -// SetRequireTLS information is found in the Mailgun documentation. -func (m *commonMessageV5) SetRequireTLS(b bool) { - m.requireTLS = b -} - -// SetSkipVerification information is found in the Mailgun documentation. -func (m *commonMessageV5) SetSkipVerification(b bool) { - m.skipVerification = b -} - -// SetTrackingOpens information is found in the Mailgun documentation. -func (m *commonMessageV5) SetTrackingOpens(trackingOpens bool) { - m.trackingOpens = &trackingOpens -} - -// SetTemplateVersion information is found in the Mailgun documentation. -func (m *commonMessageV5) SetTemplateVersion(tag string) { - m.templateVersionTag = tag -} - -// SetTemplateRenderText information is found in the Mailgun documentation. -func (m *commonMessageV5) SetTemplateRenderText(render bool) { - m.templateRenderText = render -} - -// AddHeader allows you to send custom MIME headers with the message. -func (m *commonMessageV5) AddHeader(header, value string) { - if m.headers == nil { - m.headers = make(map[string]string) - } - m.headers[header] = value -} - -// AddVariable lets you associate a set of variables with messages you send, -// which Mailgun can use to, in essence, complete form-mail. -// Refer to the Mailgun documentation for more information. -func (m *commonMessageV5) AddVariable(variable string, value any) error { - if m.variables == nil { - m.variables = make(map[string]string) - } - - j, err := json.Marshal(value) - if err != nil { - return err - } - - encoded := string(j) - v, err := strconv.Unquote(encoded) - if err != nil { - v = encoded - } - - m.variables[variable] = v - return nil -} - -// AddTemplateVariable adds a template variable to the map of template variables, replacing the variable if it is already there. -// This is used for server-side message templates and can nest arbitrary values. At send time, the resulting map will be converted into -// a JSON string and sent as a header in the X-Mailgun-Variables header. -func (m *commonMessageV5) AddTemplateVariable(variable string, value any) error { - if m.templateVariables == nil { - m.templateVariables = make(map[string]any) - } - m.templateVariables[variable] = value - return nil -} - -// AddDomain allows you to use a separate domain for the type of messages you are sending. -func (m *commonMessageV5) AddDomain(domain string) { - m.domain = domain -} - -// GetHeaders retrieves the http headers associated with this message -func (m *commonMessageV5) GetHeaders() map[string]string { - return m.headers -} - -// specific message methods - -func (m *plainMessageV5) AddRecipient(recipient string) error { - return m.AddRecipientAndVariables(recipient, nil) -} - -// AddRecipientAndVariables appends a receiver to the To: header of a message, -// and as well attaches a set of variables relevant for this recipient. -// It will return an error if the limit of recipients have been exceeded for this message -func (m *plainMessageV5) AddRecipientAndVariables(r string, vars map[string]any) error { - if m.RecipientCount() >= MaxNumberOfRecipients { - return fmt.Errorf("recipient limit exceeded (max %d)", MaxNumberOfRecipients) - } - m.to = append(m.to, r) - if vars != nil { - if m.recipientVariables == nil { - m.recipientVariables = make(map[string]map[string]any) - } - m.recipientVariables[r] = vars - } - - return nil -} - -func (m *mimeMessageV5) AddRecipient(recipient string) error { - if m.RecipientCount() >= MaxNumberOfRecipients { - return fmt.Errorf("recipient limit exceeded (max %d)", MaxNumberOfRecipients) - } - m.to = append(m.to, recipient) - - return nil -} - -func (m *plainMessageV5) RecipientCount() int { - return len(m.to) + len(m.bcc) + len(m.cc) -} - -func (m *mimeMessageV5) RecipientCount() int { - return len(m.to) -} - -// SetReplyTo sets the receiver who should receive replies -func (m *commonMessageV5) SetReplyTo(recipient string) { - m.AddHeader("Reply-To", recipient) -} - -func (m *plainMessageV5) AddCC(r string) { - m.cc = append(m.cc, r) -} - -func (*mimeMessageV5) AddCC(_ string) {} - -func (m *plainMessageV5) AddBCC(r string) { - m.bcc = append(m.bcc, r) -} - -func (*mimeMessageV5) AddBCC(_ string) {} - -func (m *plainMessageV5) SetHTML(h string) { - m.html = h -} - -func (*mimeMessageV5) SetHTML(_ string) {} - -func (m *plainMessageV5) SetAmpHTML(h string) { - m.ampHtml = h -} - -func (*mimeMessageV5) SetAmpHTML(_ string) {} - -// AddTag attaches tags to the message. Tags are useful for metrics gathering and event tracking purposes. -// Refer to the Mailgun documentation for further details. -func (m *commonMessageV5) AddTag(tag ...string) error { - if len(m.tags) >= MaxNumberOfTags { - return fmt.Errorf("cannot add any new tags. Message tag limit (%d) reached", MaxNumberOfTags) - } - - m.tags = append(m.tags, tag...) - return nil -} - -func (m *plainMessageV5) SetTemplate(t string) { - m.template = t -} - -func (*mimeMessageV5) SetTemplate(_ string) {} - -func (m *plainMessageV5) AddValues(p *FormDataPayload) { - p.addValue("from", m.from) - p.addValue("subject", m.subject) - p.addValue("text", m.text) - for _, cc := range m.cc { - p.addValue("cc", cc) - } - for _, bcc := range m.bcc { - p.addValue("bcc", bcc) - } - if m.html != "" { - p.addValue("html", m.html) - } - if m.template != "" { - p.addValue("template", m.template) - } - if m.ampHtml != "" { - p.addValue("amp-html", m.ampHtml) - } -} - -func (m *mimeMessageV5) AddValues(p *FormDataPayload) { - p.addReadCloser("message", "message.mime", m.body) -} - -func (*plainMessageV5) Endpoint() string { - return messagesEndpoint -} - -func (*mimeMessageV5) Endpoint() string { - return mimeMessagesEndpoint -} - -// Send attempts to queue a message (see CommonMessage, NewMessage, and its methods) for delivery. -// It returns the Mailgun server response, which consists of two components: -// - A human-readable status message, typically "Queued. Thank you." -// - A Message ID, which is the id used to track the queued message. The message id is useful -// when contacting support to report an issue with a specific message or to relate a -// delivered, accepted or failed event back to specific message. -// -// The status and message ID are only returned if no error occurred. -// -// Error returns can be of type `error.Error` which wrap internal and standard -// golang errors like `url.Error`. The error can also be of type -// mailgun.UnexpectedResponseError which contains the error returned by the mailgun API. -// -// mailgun.UnexpectedResponseError { -// URL: "https://api.mailgun.com/v3/messages", -// Expected: 200, -// Actual: 400, -// Data: "Domain not found: example.com", -// } -// -// See the public mailgun documentation for all possible return codes and error messages -func (mg *MailgunImpl) sendV5(ctx context.Context, m SendableMessage) (mes, id string, err error) { - // TODO(vtopc): move domain checks into NewMessage and NewMIMEMessage? - if m.Domain() == "" { - err = errors.New("you must provide a valid domain before calling Send()") - return "", "", err - } - - invalidChars := ":&'@(),!?#;%+=<>" - if i := strings.ContainsAny(m.Domain(), invalidChars); i { - err = fmt.Errorf("you called Send() with a domain that contains invalid characters") - return "", "", err - } - - if mg.apiKey == "" { - err = errors.New("you must provide a valid api-key before calling Send()") - return "", "", err - } - - if !isValid(m) { - err = ErrInvalidMessage - return "", "", err - } - - if m.STOPeriod() != "" && m.RecipientCount() > 1 { - err = errors.New("STO can only be used on a per-message basis") - return "", "", err - } - payload := NewFormDataPayload() - - m.AddValues(payload) - - // TODO: make (CommonMessage).AddValues(): - err = addMessageValues(payload, m) - if err != nil { - return "", "", err - } - - r := newHTTPRequest(generateApiV3UrlWithDomain(mg, m.Endpoint(), m.Domain())) - r.setClient(mg.HTTPClient()) - r.setBasicAuth(basicAuthUser, mg.APIKey()) - // Override any HTTP headers if provided - for k, v := range mg.overrideHeaders { - r.addHeader(k, v) - } - - var response sendMessageResponse - err = postResponseFromJSON(ctx, r, payload, &response) - if err == nil { - mes = response.Message - id = response.Id - } - - return mes, id, err -} - -func (m *plainMessageV5) isValid() bool { - if !validateStringList(m.cc, false) { - return false - } - - if !validateStringList(m.bcc, false) { - return false - } - - if m.from == "" { - return false - } - - if m.template != "" { - // m.text or m.html not needed if template is supplied - return true - } - - if m.text == "" && m.html == "" { - return false - } - - return true -} - -func (m *mimeMessageV5) isValid() bool { - return m.body != nil -} diff --git a/mock.go b/mock.go index e992085c..4b5f4134 100644 --- a/mock.go +++ b/mock.go @@ -13,6 +13,7 @@ import ( "sync" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) type MockServer interface { @@ -20,13 +21,13 @@ type MockServer interface { URL() string DomainIPS() []string DomainList() []DomainContainer - ExportList() []Export + ExportList() []mtypes.Export MailingList() []MailingListContainer - RouteList() []Route + RouteList() []mtypes.Route Events() []Event - Webhooks() WebHooksListResponse - Templates() []Template - SubaccountList() []Subaccount + Webhooks() mtypes.WebHooksListResponse + Templates() []mtypes.Template + SubaccountList() []mtypes.Subaccount } // A mailgun api mock suitable for testing @@ -35,19 +36,19 @@ type mockServer struct { domainIPS []string domainList []DomainContainer - exportList []Export + exportList []mtypes.Export mailingList []MailingListContainer - routeList []Route + routeList []mtypes.Route events []Event - templates []Template - templateVersions map[string][]TemplateVersion - unsubscribes []Unsubscribe - complaints []Complaint - bounces []Bounce - credentials []Credential - tags []Tag - subaccountList []Subaccount - webhooks WebHooksListResponse + templates []mtypes.Template + templateVersions map[string][]mtypes.TemplateVersion + unsubscribes []mtypes.Unsubscribe + complaints []mtypes.Complaint + bounces []mtypes.Bounce + credentials []mtypes.Credential + tags []mtypes.Tag + subaccountList []mtypes.Subaccount + webhooks mtypes.WebHooksListResponse mutex sync.Mutex } @@ -63,7 +64,7 @@ func (ms *mockServer) DomainList() []DomainContainer { return ms.domainList } -func (ms *mockServer) ExportList() []Export { +func (ms *mockServer) ExportList() []mtypes.Export { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.exportList @@ -75,7 +76,7 @@ func (ms *mockServer) MailingList() []MailingListContainer { return ms.mailingList } -func (ms *mockServer) RouteList() []Route { +func (ms *mockServer) RouteList() []mtypes.Route { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.routeList @@ -87,25 +88,25 @@ func (ms *mockServer) Events() []Event { return ms.events } -func (ms *mockServer) Webhooks() WebHooksListResponse { +func (ms *mockServer) Webhooks() mtypes.WebHooksListResponse { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.webhooks } -func (ms *mockServer) Templates() []Template { +func (ms *mockServer) Templates() []mtypes.Template { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.templates } -func (ms *mockServer) Unsubscribes() []Unsubscribe { +func (ms *mockServer) Unsubscribes() []mtypes.Unsubscribe { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.unsubscribes } -func (ms *mockServer) SubaccountList() []Subaccount { +func (ms *mockServer) SubaccountList() []mtypes.Subaccount { defer ms.mutex.Unlock() ms.mutex.Lock() return ms.subaccountList @@ -286,3 +287,8 @@ func randomString(n int, prefix string) string { func randomEmail(prefix, domain string) string { return strings.ToLower(fmt.Sprintf("%s@%s", randomString(20, prefix), domain)) } + +type okResp struct { + ID string `json:"id,omitempty"` + Message string `json:"message"` +} diff --git a/mock_analytics.go b/mock_analytics.go index 868bc5b6..bf51a0a9 100644 --- a/mock_analytics.go +++ b/mock_analytics.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addAnalyticsRoutes(r chi.Router) { @@ -11,23 +12,23 @@ func (ms *mockServer) addAnalyticsRoutes(r chi.Router) { } func (ms *mockServer) listMetrics(w http.ResponseWriter, _ *http.Request) { - start, _ := NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000") - end, _ := NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000") + start, _ := mtypes.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000") + end, _ := mtypes.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000") - resp := MetricsResponse{ + resp := mtypes.MetricsResponse{ Start: start, End: end, Resolution: "day", Duration: "30d", Dimensions: []string{"time"}, - Items: []MetricsItem{ + Items: []mtypes.MetricsItem{ { - Dimensions: []MetricsDimension{{ + Dimensions: []mtypes.MetricsDimension{{ Dimension: "time", Value: "Tue, 24 Sep 2024 00:00:00 +0000", DisplayValue: "Tue, 24 Sep 2024 00:00:00 +0000", }}, - Metrics: Metrics{ + Metrics: mtypes.Metrics{ SentCount: ptr(uint64(4)), DeliveredCount: ptr(uint64(3)), OpenedCount: ptr(uint64(2)), @@ -35,7 +36,7 @@ func (ms *mockServer) listMetrics(w http.ResponseWriter, _ *http.Request) { }, }, }, - Pagination: MetricsPagination{ + Pagination: mtypes.MetricsPagination{ Sort: "", Skip: 0, Limit: 10, diff --git a/mock_bounces.go b/mock_bounces.go index ecea4cbd..54d1c4d9 100644 --- a/mock_bounces.go +++ b/mock_bounces.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addBouncesRoutes(r chi.Router) { @@ -18,15 +19,15 @@ func (ms *mockServer) addBouncesRoutes(r chi.Router) { r.Delete("/{domain}/bounces", ms.deleteBouncesList) r.Post("/{domain}/bounces", ms.createBounce) - ms.bounces = append(ms.bounces, Bounce{ - CreatedAt: RFC2822Time(time.Now()), + ms.bounces = append(ms.bounces, mtypes.Bounce{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Error: "invalid address", Code: "INVALID", Address: "foo@mailgun.test", }) - ms.bounces = append(ms.bounces, Bounce{ - CreatedAt: RFC2822Time(time.Now()), + ms.bounces = append(ms.bounces, mtypes.Bounce{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Error: "non existing address", Code: "NOT_EXIST", Address: "alice@example.com", @@ -59,20 +60,20 @@ func (ms *mockServer) listBounces(w http.ResponseWriter, r *http.Request) { } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []Bounce + var results []mtypes.Bounce if start != end { results = ms.bounces[start:end] nextAddress = results[len(results)-1].Address prevAddress = results[0].Address } else { - results = []Bounce{} + results = []mtypes.Bounce{} nextAddress = pivot prevAddress = pivot } toJSON(w, bouncesListResponse{ - Paging: Paging{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -110,7 +111,7 @@ func (ms *mockServer) createBounce(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var bounces []Bounce + var bounces []mtypes.Bounce if r.Header.Get("Content-Type") == "application/json" { body, err := ioutil.ReadAll(r.Body) if err != nil { @@ -142,7 +143,7 @@ func (ms *mockServer) createBounce(w http.ResponseWriter, r *http.Request) { return } - bounces = append(bounces, Bounce{Address: address, Code: code, Error: bounceError}) + bounces = append(bounces, mtypes.Bounce{Address: address, Code: code, Error: bounceError}) } for _, bounce := range bounces { @@ -188,7 +189,7 @@ func (ms *mockServer) deleteBouncesList(w http.ResponseWriter, r *http.Request) defer ms.mutex.Unlock() ms.mutex.Lock() - ms.bounces = []Bounce{} + ms.bounces = []mtypes.Bounce{} toJSON(w, map[string]any{ "message": "All bounces has been deleted", diff --git a/mock_complaints.go b/mock_complaints.go index fdc9f98d..2920dcdf 100644 --- a/mock_complaints.go +++ b/mock_complaints.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addComplaintsRoutes(r chi.Router) { @@ -17,13 +18,13 @@ func (ms *mockServer) addComplaintsRoutes(r chi.Router) { r.Delete("/{domain}/complaints/{address}", ms.deleteComplaint) r.Post("/{domain}/complaints", ms.createComplaint) - ms.complaints = append(ms.complaints, Complaint{ - CreatedAt: RFC2822Time(time.Now()), + ms.complaints = append(ms.complaints, mtypes.Complaint{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Address: "foo@mailgun.test", }) - ms.complaints = append(ms.complaints, Complaint{ - CreatedAt: RFC2822Time(time.Now()), + ms.complaints = append(ms.complaints, mtypes.Complaint{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Address: "alice@example.com", }) } @@ -54,20 +55,20 @@ func (ms *mockServer) listComplaints(w http.ResponseWriter, r *http.Request) { } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []Complaint + var results []mtypes.Complaint if start != end { results = ms.complaints[start:end] nextAddress = results[len(results)-1].Address prevAddress = results[0].Address } else { - results = []Complaint{} + results = []mtypes.Complaint{} nextAddress = pivot prevAddress = pivot } - toJSON(w, complaintsResponse{ - Paging: Paging{ + toJSON(w, mtypes.ComplaintsResponse{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -105,7 +106,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var complaints []Complaint + var complaints []mtypes.Complaint if r.Header.Get("Content-Type") == "application/json" { body, err := ioutil.ReadAll(r.Body) if err != nil { @@ -134,7 +135,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) { return } - complaints = append(complaints, Complaint{Address: address, CreatedAt: RFC2822Time(time.Now())}) + complaints = append(complaints, mtypes.Complaint{Address: address, CreatedAt: mtypes.RFC2822Time(time.Now())}) } for _, complaint := range complaints { @@ -146,7 +147,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) { } if !addressExist { - complaint.CreatedAt = RFC2822Time(time.Now()) + complaint.CreatedAt = mtypes.RFC2822Time(time.Now()) ms.complaints = append(ms.complaints, complaint) } } diff --git a/mock_credentials.go b/mock_credentials.go index 0e9b6ff1..4fdb4594 100644 --- a/mock_credentials.go +++ b/mock_credentials.go @@ -5,6 +5,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addCredentialsRoutes(r chi.Router) { @@ -13,14 +14,14 @@ func (ms *mockServer) addCredentialsRoutes(r chi.Router) { r.Delete("/domains/{domain}/credentials/{login}", ms.deleteCredential) r.Post("/domains/{domain}/credentials", ms.createCredential) - ms.credentials = append(ms.credentials, Credential{ - CreatedAt: RFC2822Time(time.Now()), + ms.credentials = append(ms.credentials, mtypes.Credential{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Login: "alice", Password: "alices_password", }) - ms.credentials = append(ms.credentials, Credential{ - CreatedAt: RFC2822Time(time.Now()), + ms.credentials = append(ms.credentials, mtypes.Credential{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Login: "bob", Password: "bobs_password", }) @@ -37,7 +38,7 @@ func (ms *mockServer) listCredentials(w http.ResponseWriter, r *http.Request) { skip := stringToInt(r.FormValue("skip")) - var results []Credential + var results []mtypes.Credential if skip > 0 { if len(ms.credentials[skip:]) < limit { @@ -92,7 +93,7 @@ func (ms *mockServer) createCredential(w http.ResponseWriter, r *http.Request) { } } - ms.credentials = append(ms.credentials, Credential{Login: login, Password: password, CreatedAt: RFC2822Time(time.Now())}) + ms.credentials = append(ms.credentials, mtypes.Credential{Login: login, Password: password, CreatedAt: mtypes.RFC2822Time(time.Now())}) toJSON(w, map[string]any{ "message": "Credentials created", diff --git a/mock_domains.go b/mock_domains.go index 74599e0c..7065c2d4 100644 --- a/mock_domains.go +++ b/mock_domains.go @@ -5,47 +5,48 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) type DomainContainer struct { - Domain Domain `json:"domain"` - ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"` - SendingDNSRecords []DNSRecord `json:"sending_dns_records"` - Connection *DomainConnection `json:"connection,omitempty"` - Tracking *DomainTracking `json:"tracking,omitempty"` - TagLimits *TagLimits `json:"limits,omitempty"` + Domain mtypes.Domain `json:"domain"` + ReceivingDNSRecords []mtypes.DNSRecord `json:"receiving_dns_records"` + SendingDNSRecords []mtypes.DNSRecord `json:"sending_dns_records"` + Connection *mtypes.DomainConnection `json:"connection,omitempty"` + Tracking *mtypes.DomainTracking `json:"tracking,omitempty"` + TagLimits *mtypes.TagLimits `json:"limits,omitempty"` } func (ms *mockServer) addDomainRoutes(r chi.Router) { ms.domainList = append(ms.domainList, DomainContainer{ - Domain: Domain{ - CreatedAt: RFC2822Time(time.Now().UTC()), + Domain: mtypes.Domain{ + CreatedAt: mtypes.RFC2822Time(time.Now().UTC()), Name: "mailgun.test", SMTPLogin: "postmaster@mailgun.test", SMTPPassword: "4rtqo4p6rrx9", Wildcard: true, - SpamAction: SpamActionDisabled, + SpamAction: mtypes.SpamActionDisabled, State: "active", WebScheme: "http", }, - Connection: &DomainConnection{ + Connection: &mtypes.DomainConnection{ RequireTLS: true, SkipVerification: true, }, - TagLimits: &TagLimits{ + TagLimits: &mtypes.TagLimits{ Limit: 50000, Count: 5000, }, - Tracking: &DomainTracking{ - Click: TrackingStatus{Active: true}, - Open: TrackingStatus{Active: true}, - Unsubscribe: TrackingStatus{ + Tracking: &mtypes.DomainTracking{ + Click: mtypes.TrackingStatus{Active: true}, + Open: mtypes.TrackingStatus{Active: true}, + Unsubscribe: mtypes.TrackingStatus{ Active: false, HTMLFooter: "\n
\n

unsubscribe

\n", TextFooter: "\n\nTo unsubscribe click: <%unsubscribe_url%>\n\n", }, }, - ReceivingDNSRecords: []DNSRecord{ + ReceivingDNSRecords: []mtypes.DNSRecord{ { Priority: "10", RecordType: "MX", @@ -59,7 +60,7 @@ func (ms *mockServer) addDomainRoutes(r chi.Router) { Value: "mxb.mailgun.org", }, }, - SendingDNSRecords: []DNSRecord{ + SendingDNSRecords: []mtypes.DNSRecord{ { RecordType: "TXT", Valid: "valid", @@ -104,7 +105,7 @@ func (ms *mockServer) listDomains(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var list []Domain + var list []mtypes.Domain for _, domain := range ms.domainList { list = append(list, domain.Domain) } @@ -126,14 +127,14 @@ func (ms *mockServer) listDomains(w http.ResponseWriter, r *http.Request) { // If we are at the end of the list if skip == end { - toJSON(w, domainsListResponse{ + toJSON(w, mtypes.ListDomainsResponse{ TotalCount: len(list), - Items: []Domain{}, + Items: []mtypes.Domain{}, }) return } - toJSON(w, domainsListResponse{ + toJSON(w, mtypes.ListDomainsResponse{ TotalCount: len(list), Items: list[skip:end], }) @@ -159,13 +160,13 @@ func (ms *mockServer) createDomain(w http.ResponseWriter, r *http.Request) { ms.mutex.Lock() ms.domainList = append(ms.domainList, DomainContainer{ - Domain: Domain{ - CreatedAt: RFC2822Time(time.Now()), + Domain: mtypes.Domain{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Name: r.FormValue("name"), SMTPLogin: r.FormValue("smtp_login"), SMTPPassword: r.FormValue("smtp_password"), Wildcard: stringToBool(r.FormValue("wildcard")), - SpamAction: SpamAction(r.FormValue("spam_action")), + SpamAction: mtypes.SpamAction(r.FormValue("spam_action")), State: "active", WebScheme: "http", }, @@ -214,7 +215,7 @@ func (ms *mockServer) getConnection(w http.ResponseWriter, r *http.Request) { for _, d := range ms.domainList { if d.Domain.Name == chi.URLParam(r, "domain") { - resp := domainConnectionResponse{ + resp := mtypes.DomainConnectionResponse{ Connection: *d.Connection, } toJSON(w, resp) @@ -231,7 +232,7 @@ func (ms *mockServer) updateConnection(w http.ResponseWriter, r *http.Request) { for i, d := range ms.domainList { if d.Domain.Name == chi.URLParam(r, "domain") { - ms.domainList[i].Connection = &DomainConnection{ + ms.domainList[i].Connection = &mtypes.DomainConnection{ RequireTLS: stringToBool(r.FormValue("require_tls")), SkipVerification: stringToBool(r.FormValue("skip_verification")), } @@ -249,7 +250,7 @@ func (ms *mockServer) getTracking(w http.ResponseWriter, r *http.Request) { for _, d := range ms.domainList { if d.Domain.Name == chi.URLParam(r, "domain") { - resp := domainTrackingResponse{ + resp := mtypes.DomainTrackingResponse{ Tracking: *d.Tracking, } toJSON(w, resp) diff --git a/mock_events.go b/mock_events.go index c0a88a1a..f0a91bcf 100644 --- a/mock_events.go +++ b/mock_events.go @@ -7,6 +7,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/mailgun/mailgun-go/v4/events" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addEventRoutes(r chi.Router) { @@ -190,8 +191,8 @@ func (ms *mockServer) addEventRoutes(r chi.Router) { } type eventsResponse struct { - Items []Event `json:"items"` - Paging Paging `json:"paging"` + Items []Event `json:"items"` + Paging mtypes.Paging `json:"paging"` } func (ms *mockServer) listEvents(w http.ResponseWriter, r *http.Request) { @@ -223,7 +224,7 @@ func (ms *mockServer) listEvents(w http.ResponseWriter, r *http.Request) { } resp := eventsResponse{ - Paging: Paging{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), diff --git a/mock_exports.go b/mock_exports.go index 737d5cf3..9865246b 100644 --- a/mock_exports.go +++ b/mock_exports.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addExportRoutes(r chi.Router) { @@ -17,7 +18,7 @@ func (ms *mockServer) addExportRoutes(r chi.Router) { func (ms *mockServer) postExports(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - e := Export{ + e := mtypes.Export{ ID: strconv.Itoa(len(ms.exportList)), URL: r.FormValue("url"), Status: "complete", @@ -30,7 +31,7 @@ func (ms *mockServer) postExports(w http.ResponseWriter, r *http.Request) { func (ms *mockServer) listExports(w http.ResponseWriter, _ *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - toJSON(w, ExportList{ + toJSON(w, mtypes.ExportList{ Items: ms.exportList, }) } diff --git a/mock_ips.go b/mock_ips.go index 12fbd19b..6a31d41e 100644 --- a/mock_ips.go +++ b/mock_ips.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addIPRoutes(r chi.Router) { @@ -18,14 +19,14 @@ func (ms *mockServer) addIPRoutes(r chi.Router) { } func (ms *mockServer) listIPS(w http.ResponseWriter, _ *http.Request) { - toJSON(w, ipAddressListResponse{ + toJSON(w, mtypes.IPAddressListResponse{ TotalCount: 2, Items: []string{"172.0.0.1", "192.168.1.1"}, }) } func (ms *mockServer) getIPAddress(w http.ResponseWriter, r *http.Request) { - toJSON(w, IPAddress{ + toJSON(w, mtypes.IPAddress{ IP: chi.URLParam(r, "ip"), RDNS: "luna.mailgun.net", Dedicated: true, @@ -35,7 +36,7 @@ func (ms *mockServer) getIPAddress(w http.ResponseWriter, r *http.Request) { func (ms *mockServer) listDomainIPS(w http.ResponseWriter, _ *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - toJSON(w, ipAddressListResponse{ + toJSON(w, mtypes.IPAddressListResponse{ TotalCount: 2, Items: ms.domainIPS, }) diff --git a/mock_mailing_list.go b/mock_mailing_list.go index 54822640..1a33c640 100644 --- a/mock_mailing_list.go +++ b/mock_mailing_list.go @@ -7,11 +7,12 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) type MailingListContainer struct { - MailingList MailingList - Members []Member + MailingList mtypes.MailingList + Members []mtypes.Member } func (ms *mockServer) addMailingListRoutes(r chi.Router) { @@ -29,16 +30,16 @@ func (ms *mockServer) addMailingListRoutes(r chi.Router) { r.Post("/lists/{address}/members.json", ms.bulkCreate) ms.mailingList = append(ms.mailingList, MailingListContainer{ - MailingList: MailingList{ + MailingList: mtypes.MailingList{ ReplyPreference: "list", AccessLevel: "everyone", Address: "foo@mailgun.test", - CreatedAt: RFC2822Time(time.Now().UTC()), + CreatedAt: mtypes.RFC2822Time(time.Now().UTC()), Description: "Mailgun developers list", MembersCount: 1, Name: "", }, - Members: []Member{ + Members: []mtypes.Member{ { Address: "dev@samples.mailgun.org", Name: "Developer", @@ -51,7 +52,7 @@ func (ms *mockServer) listMailingLists(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var list []MailingList + var list []mtypes.MailingList var idx []string for _, ml := range ms.mailingList { @@ -67,12 +68,12 @@ func (ms *mockServer) listMailingLists(w http.ResponseWriter, r *http.Request) { results := list[start:end] if len(results) == 0 { - toJSON(w, listsResponse{}) + toJSON(w, mtypes.ListMailingListsResponse{}) return } - resp := listsResponse{ - Paging: Paging{ + resp := mtypes.ListMailingListsResponse{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -99,7 +100,7 @@ func (ms *mockServer) getMailingList(w http.ResponseWriter, r *http.Request) { for _, ml := range ms.mailingList { if ml.MailingList.Address == chi.URLParam(r, "address") { - toJSON(w, mailingListResponse{MailingList: ml.MailingList}) + toJSON(w, mtypes.GetMailingListResponse{MailingList: ml.MailingList}) return } } @@ -145,10 +146,10 @@ func (ms *mockServer) updateMailingList(w http.ResponseWriter, r *http.Request) ms.mailingList[i].MailingList.Description = r.FormValue("description") } if r.FormValue("access_level") != "" { - ms.mailingList[i].MailingList.AccessLevel = AccessLevel(r.FormValue("access_level")) + ms.mailingList[i].MailingList.AccessLevel = mtypes.AccessLevel(r.FormValue("access_level")) } if r.FormValue("reply_preference") != "" { - ms.mailingList[i].MailingList.ReplyPreference = ReplyPreference(r.FormValue("reply_preference")) + ms.mailingList[i].MailingList.ReplyPreference = mtypes.ReplyPreference(r.FormValue("reply_preference")) } toJSON(w, okResp{Message: "Mailing list member has been updated"}) return @@ -163,13 +164,13 @@ func (ms *mockServer) createMailingList(w http.ResponseWriter, r *http.Request) ms.mutex.Lock() ms.mailingList = append(ms.mailingList, MailingListContainer{ - MailingList: MailingList{ - CreatedAt: RFC2822Time(time.Now().UTC()), + MailingList: mtypes.MailingList{ + CreatedAt: mtypes.RFC2822Time(time.Now().UTC()), Name: r.FormValue("name"), Address: r.FormValue("address"), Description: r.FormValue("description"), - AccessLevel: AccessLevel(r.FormValue("access_level")), - ReplyPreference: ReplyPreference(r.FormValue("reply_preference")), + AccessLevel: mtypes.AccessLevel(r.FormValue("access_level")), + ReplyPreference: mtypes.ReplyPreference(r.FormValue("reply_preference")), }, }) toJSON(w, okResp{Message: "Mailing list has been created"}) @@ -179,7 +180,7 @@ func (ms *mockServer) listMembers(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var list []Member + var list []mtypes.Member var idx []string var found bool @@ -207,12 +208,12 @@ func (ms *mockServer) listMembers(w http.ResponseWriter, r *http.Request) { results := list[start:end] if len(results) == 0 { - toJSON(w, memberListResponse{}) + toJSON(w, mtypes.MemberListResponse{}) return } - resp := memberListResponse{ - Paging: Paging{ + resp := mtypes.MemberListResponse{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -243,7 +244,7 @@ func (ms *mockServer) getMember(w http.ResponseWriter, r *http.Request) { found = true for _, member := range ml.Members { if member.Address == chi.URLParam(r, "member") { - toJSON(w, memberResponse{Member: member}) + toJSON(w, mtypes.MemberResponse{Member: member}) return } } @@ -372,7 +373,7 @@ func (ms *mockServer) createMember(w http.ResponseWriter, r *http.Request) { } } - ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, Member{ + ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, mtypes.Member{ Name: r.FormValue("name"), Address: parseAddress(r.FormValue("address")), Vars: stringToMap(r.FormValue("vars")), @@ -398,7 +399,7 @@ func (ms *mockServer) bulkCreate(w http.ResponseWriter, r *http.Request) { return } - var bulkList []Member + var bulkList []mtypes.Member if err := json.Unmarshal([]byte(r.FormValue("members")), &bulkList); err != nil { w.WriteHeader(http.StatusInternalServerError) toJSON(w, okResp{Message: "while un-marshalling 'members' param - " + err.Error()}) diff --git a/mock_messages.go b/mock_messages.go index 539a73a5..26b35b1d 100644 --- a/mock_messages.go +++ b/mock_messages.go @@ -8,6 +8,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/mailgun/mailgun-go/v4/events" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addMessagesRoutes(r chi.Router) { @@ -94,7 +95,7 @@ func (ms *mockServer) createMessages(w http.ResponseWriter, r *http.Request) { } if !tagExists { - ms.tags = append(ms.tags, Tag{Value: newTag}) + ms.tags = append(ms.tags, mtypes.Tag{Value: newTag}) } } @@ -119,7 +120,7 @@ func (ms *mockServer) getStoredMessages(w http.ResponseWriter, r *http.Request) toJSON(w, okResp{Message: "not found"}) } - toJSON(w, StoredMessage{ + toJSON(w, mtypes.StoredMessage{ Recipients: strings.Join(stored.Message.Recipients, ","), Sender: stored.Message.Headers.From, Subject: stored.Message.Headers.Subject, diff --git a/mock_routes.go b/mock_routes.go index ada658c3..1a59f952 100644 --- a/mock_routes.go +++ b/mock_routes.go @@ -6,10 +6,11 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) type routeResponse struct { - Route Route `json:"route"` + Route mtypes.Route `json:"route"` } func (ms *mockServer) addRoutes(r chi.Router) { @@ -20,7 +21,7 @@ func (ms *mockServer) addRoutes(r chi.Router) { r.Delete("/routes/{id}", ms.deleteRoute) for i := 0; i < 10; i++ { - ms.routeList = append(ms.routeList, Route{ + ms.routeList = append(ms.routeList, mtypes.Route{ Id: randomString(10, "ID-"), Priority: 0, Description: fmt.Sprintf("Sample Route %d", i), @@ -51,14 +52,14 @@ func (ms *mockServer) listRoutes(w http.ResponseWriter, r *http.Request) { // If we are at the end of the list if skip == end { - toJSON(w, routesListResponse{ + toJSON(w, mtypes.RoutesListResponse{ TotalCount: len(ms.routeList), - Items: []Route{}, + Items: []mtypes.Route{}, }) return } - toJSON(w, routesListResponse{ + toJSON(w, mtypes.RoutesListResponse{ TotalCount: len(ms.routeList), Items: ms.routeList[skip:end], }) @@ -85,15 +86,15 @@ func (ms *mockServer) createRoute(w http.ResponseWriter, r *http.Request) { return } - ms.routeList = append(ms.routeList, Route{ - CreatedAt: RFC2822Time(time.Now().UTC()), + ms.routeList = append(ms.routeList, mtypes.Route{ + CreatedAt: mtypes.RFC2822Time(time.Now().UTC()), Id: randomString(10, "ID-"), Priority: stringToInt(r.FormValue("priority")), Description: r.FormValue("description"), Expression: r.FormValue("expression"), Actions: r.Form["action"], }) - toJSON(w, createRouteResp{ + toJSON(w, mtypes.CreateRouteResp{ Message: "Route has been created", Route: ms.routeList[len(ms.routeList)-1], }) diff --git a/mock_subaccounts.go b/mock_subaccounts.go index c6141b28..357ce962 100644 --- a/mock_subaccounts.go +++ b/mock_subaccounts.go @@ -4,15 +4,16 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addSubaccountRoutes(r chi.Router) { - ms.subaccountList = append(ms.subaccountList, Subaccount{ - Id: "enabled.subaccount", + ms.subaccountList = append(ms.subaccountList, mtypes.Subaccount{ + ID: "enabled.subaccount", Name: "mailgun.test", Status: "enabled", - }, Subaccount{ - Id: "disabled.subaccount", + }, mtypes.Subaccount{ + ID: "disabled.subaccount", Name: "mailgun.test", Status: "disabled", }) @@ -29,7 +30,7 @@ func (ms *mockServer) listSubaccounts(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - var list subaccountsListResponse + var list mtypes.ListSubaccountsResponse for _, subaccount := range ms.subaccountList { list.Items = append(list.Items, subaccount) } @@ -51,14 +52,14 @@ func (ms *mockServer) listSubaccounts(w http.ResponseWriter, r *http.Request) { // If we are at the end of the list if skip == end { - toJSON(w, subaccountsListResponse{ + toJSON(w, mtypes.ListSubaccountsResponse{ Total: len(list.Items), - Items: []Subaccount{}, + Items: []mtypes.Subaccount{}, }) return } - toJSON(w, subaccountsListResponse{ + toJSON(w, mtypes.ListSubaccountsResponse{ Total: len(list.Items), Items: list.Items[skip:end], }) @@ -69,8 +70,8 @@ func (ms *mockServer) getSubaccount(w http.ResponseWriter, r *http.Request) { ms.mutex.Lock() for _, s := range ms.subaccountList { - if s.Id == chi.URLParam(r, "subaccountID") { - toJSON(w, SubaccountResponse{Item: s}) + if s.ID == chi.URLParam(r, "subaccountID") { + toJSON(w, mtypes.SubaccountResponse{Item: s}) return } } @@ -82,8 +83,8 @@ func (ms *mockServer) createSubaccount(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - ms.subaccountList = append(ms.subaccountList, Subaccount{ - Id: "test", + ms.subaccountList = append(ms.subaccountList, mtypes.Subaccount{ + ID: "test", Name: r.FormValue("name"), Status: "active", }) @@ -95,12 +96,12 @@ func (ms *mockServer) enableSubaccount(w http.ResponseWriter, r *http.Request) { ms.mutex.Lock() for _, subaccount := range ms.subaccountList { - if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" { + if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" { subaccount.Status = "enabled" - toJSON(w, SubaccountResponse{Item: subaccount}) + toJSON(w, mtypes.SubaccountResponse{Item: subaccount}) return } - if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" { + if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" { toJSON(w, okResp{Message: "subaccount is already enabled"}) return } @@ -113,12 +114,12 @@ func (ms *mockServer) disableSubaccount(w http.ResponseWriter, r *http.Request) ms.mutex.Lock() for _, subaccount := range ms.subaccountList { - if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" { + if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" { subaccount.Status = "disabled" - toJSON(w, SubaccountResponse{Item: subaccount}) + toJSON(w, mtypes.SubaccountResponse{Item: subaccount}) return } - if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" { + if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" { toJSON(w, okResp{Message: "subaccount is already disabled"}) return } diff --git a/mock_tags.go b/mock_tags.go index 09e0d856..5082506b 100644 --- a/mock_tags.go +++ b/mock_tags.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addTagsRoutes(r chi.Router) { @@ -16,14 +17,14 @@ func (ms *mockServer) addTagsRoutes(r chi.Router) { tenMinutesBefore := time.Now().Add(-10 * time.Minute) now := time.Now() - ms.tags = append(ms.tags, Tag{ + ms.tags = append(ms.tags, mtypes.Tag{ Value: "test", Description: "test description", FirstSeen: &tenMinutesBefore, LastSeen: &now, }) - ms.tags = append(ms.tags, Tag{ + ms.tags = append(ms.tags, mtypes.Tag{ Value: "test2", Description: "test2 description", FirstSeen: &tenMinutesBefore, @@ -57,20 +58,20 @@ func (ms *mockServer) listTags(w http.ResponseWriter, r *http.Request) { } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []Tag + var results []mtypes.Tag if start != end { results = ms.tags[start:end] nextAddress = results[len(results)-1].Value prevAddress = results[0].Value } else { - results = []Tag{} + results = []mtypes.Tag{} nextAddress = pivot prevAddress = pivot } - toJSON(w, tagsResponse{ - Paging: Paging{ + toJSON(w, mtypes.TagsResponse{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -125,7 +126,7 @@ func (ms *mockServer) createUpdateTags(w http.ResponseWriter, r *http.Request) { } if !tagExists { - ms.tags = append(ms.tags, Tag{Value: tag, Description: description}) + ms.tags = append(ms.tags, mtypes.Tag{Value: tag, Description: description}) } toJSON(w, map[string]any{ diff --git a/mock_template_versions.go b/mock_template_versions.go index f3881002..757e9f1b 100644 --- a/mock_template_versions.go +++ b/mock_template_versions.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addTemplateVersionRoutes(r chi.Router) { @@ -54,20 +55,20 @@ func (ms *mockServer) listTemplateVersions(w http.ResponseWriter, r *http.Reques } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []TemplateVersion + var results []mtypes.TemplateVersion if start != end { results = ms.templateVersions[templateName][start:end] nextAddress = results[len(results)-1].Tag prevAddress = results[0].Tag } else { - results = []TemplateVersion{} + results = []mtypes.TemplateVersion{} nextAddress = pivot prevAddress = pivot } - toJSON(w, templateVersionListResp{ - Paging: Paging{ + toJSON(w, mtypes.TemplateVersionListResp{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -84,8 +85,8 @@ func (ms *mockServer) listTemplateVersions(w http.ResponseWriter, r *http.Reques }), }, Template: struct { - Template - Versions []TemplateVersion `json:"versions,omitempty"` + mtypes.Template + Versions []mtypes.TemplateVersion `json:"versions,omitempty"` }{ Template: template, Versions: results, @@ -118,7 +119,7 @@ func (ms *mockServer) getTemplateVersion(w http.ResponseWriter, r *http.Request) template.Version = templateVersion - toJSON(w, &templateResp{ + toJSON(w, &mtypes.TemplateResp{ Item: template, }) } @@ -172,12 +173,12 @@ func (ms *mockServer) createTemplateVersion(w http.ResponseWriter, r *http.Reque engine = "handlebars" } - newTemplateVersion := TemplateVersion{ + newTemplateVersion := mtypes.TemplateVersion{ Template: templateContent, Comment: comment, Tag: tagName, - Engine: TemplateEngine(engine), - CreatedAt: RFC2822Time(time.Now()), + Engine: mtypes.TemplateEngine(engine), + CreatedAt: mtypes.RFC2822Time(time.Now()), } if active == "yes" { @@ -212,7 +213,7 @@ func (ms *mockServer) updateTemplateVersion(w http.ResponseWriter, r *http.Reque } var templateVersionFound bool - var templateVersion TemplateVersion + var templateVersion mtypes.TemplateVersion var templateVersionIndex int for i, tmplVersion := range ms.templateVersions[templateName] { if tmplVersion.Tag == templateVersionName { @@ -308,7 +309,7 @@ func (ms *mockServer) deleteTemplateVersion(w http.ResponseWriter, r *http.Reque }) } -func (ms *mockServer) fetchTemplate(name string) (template Template, found bool) { +func (ms *mockServer) fetchTemplate(name string) (template mtypes.Template, found bool) { for _, existingTemplate := range ms.templates { if existingTemplate.Name == name { template = existingTemplate @@ -316,15 +317,15 @@ func (ms *mockServer) fetchTemplate(name string) (template Template, found bool) } } - return Template{}, false + return mtypes.Template{}, false } -func (ms *mockServer) fetchTemplateVersion(templateName string, templateVersionTag string) (TemplateVersion, bool) { +func (ms *mockServer) fetchTemplateVersion(templateName string, templateVersionTag string) (mtypes.TemplateVersion, bool) { for _, existingTemplate := range ms.templateVersions[templateName] { if existingTemplate.Tag == templateVersionTag { return existingTemplate, true } } - return TemplateVersion{}, false + return mtypes.TemplateVersion{}, false } diff --git a/mock_templates.go b/mock_templates.go index fffea13a..357378d6 100644 --- a/mock_templates.go +++ b/mock_templates.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addTemplateRoutes(r chi.Router) { @@ -19,53 +20,53 @@ func (ms *mockServer) addTemplateRoutes(r chi.Router) { r.Delete("/{domain}/templates/{name}", ms.deleteTemplate) r.Delete("/{domain}/templates/{name}", ms.deleteAllTemplates) - ms.templates = append(ms.templates, Template{ + ms.templates = append(ms.templates, mtypes.Template{ Name: "template1", Description: "template1 description", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), }) - ms.templateVersions = make(map[string][]TemplateVersion) - ms.templateVersions["template1"] = []TemplateVersion{ + ms.templateVersions = make(map[string][]mtypes.TemplateVersion) + ms.templateVersions["template1"] = []mtypes.TemplateVersion{ { Tag: "test", Template: "template1 content", Engine: "go", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), Comment: "template1 comment", Active: true, }, } - ms.templates = append(ms.templates, Template{ + ms.templates = append(ms.templates, mtypes.Template{ Name: "template2", Description: "template2 description", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), }) - ms.templateVersions["template2"] = []TemplateVersion{ + ms.templateVersions["template2"] = []mtypes.TemplateVersion{ { Tag: "test", Template: "template2 content", Engine: "go", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), Comment: "template2 comment", Active: false, }, } - ms.templates = append(ms.templates, Template{ + ms.templates = append(ms.templates, mtypes.Template{ Name: "template3", Description: "template3 description", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), }) - ms.templateVersions["template3"] = []TemplateVersion{ + ms.templateVersions["template3"] = []mtypes.TemplateVersion{ { Tag: "test", Template: "template3 content", Engine: "go", - CreatedAt: RFC2822Time(time.Now()), + CreatedAt: mtypes.RFC2822Time(time.Now()), Comment: "template3 comment", Active: false, }, @@ -98,20 +99,20 @@ func (ms *mockServer) listTemplates(w http.ResponseWriter, r *http.Request) { } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []Template + var results []mtypes.Template if start != end { results = ms.templates[start:end] nextAddress = results[len(results)-1].Name prevAddress = results[0].Name } else { - results = []Template{} + results = []mtypes.Template{} nextAddress = pivot prevAddress = pivot } - toJSON(w, templateListResp{ - Paging: Paging{ + toJSON(w, mtypes.ListTemplateResp{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -154,7 +155,7 @@ func (ms *mockServer) getTemplate(w http.ResponseWriter, r *http.Request) { template.Version = version } } - toJSON(w, &templateResp{ + toJSON(w, &mtypes.TemplateResp{ Item: template, }) return @@ -187,8 +188,8 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) { name = strings.ToLower(name) - template := Template{Name: name} - template.CreatedAt = RFC2822Time(time.Now()) + template := mtypes.Template{Name: name} + template.CreatedAt = mtypes.RFC2822Time(time.Now()) description := r.FormValue("description") if len(description) > 0 { @@ -197,7 +198,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) { templateContent := r.FormValue("template") if len(templateContent) > 0 { - templateVersion := TemplateVersion{Template: templateContent} + templateVersion := mtypes.TemplateVersion{Template: templateContent} tag := r.FormValue("tag") if len(tag) > 0 { templateVersion.Tag = tag @@ -206,7 +207,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) { } templateVersion.Comment = r.FormValue("comment") - templateVersion.CreatedAt = RFC2822Time(time.Now()) + templateVersion.CreatedAt = mtypes.RFC2822Time(time.Now()) templateVersion.Active = true engine := r.FormValue("engine") @@ -216,7 +217,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("{\"message\": \"Invalid parameter: engine %s is not supported\"}", engine))) return } - templateVersion.Engine = TemplateEngine(engine) + templateVersion.Engine = mtypes.TemplateEngine(engine) } template.Version = templateVersion @@ -296,18 +297,18 @@ func (ms *mockServer) deleteAllTemplates(w http.ResponseWriter, r *http.Request) defer ms.mutex.Unlock() ms.mutex.Lock() - ms.templates = []Template{} - ms.templateVersions = map[string][]TemplateVersion{} + ms.templates = []mtypes.Template{} + ms.templateVersions = map[string][]mtypes.TemplateVersion{} toJSON(w, map[string]string{"message": "templates have been deleted"}) } -func (ms *mockServer) getActiveTemplateVersion(templateName string) TemplateVersion { +func (ms *mockServer) getActiveTemplateVersion(templateName string) mtypes.TemplateVersion { for _, templateVersion := range ms.templateVersions[templateName] { if templateVersion.Active { return templateVersion } } - return TemplateVersion{} + return mtypes.TemplateVersion{} } diff --git a/mock_unsubscribes.go b/mock_unsubscribes.go index 80917630..7827825a 100644 --- a/mock_unsubscribes.go +++ b/mock_unsubscribes.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addUnsubscribesRoutes(r chi.Router) { @@ -17,15 +18,15 @@ func (ms *mockServer) addUnsubscribesRoutes(r chi.Router) { r.Delete("/{domain}/unsubscribes/{address}", ms.deleteUnsubscribe) r.Post("/{domain}/unsubscribes", ms.createUnsubscribe) - ms.unsubscribes = append(ms.unsubscribes, Unsubscribe{ - CreatedAt: RFC2822Time(time.Now()), + ms.unsubscribes = append(ms.unsubscribes, mtypes.Unsubscribe{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Tags: []string{"*"}, ID: "1", Address: "foo@mailgun.test", }) - ms.unsubscribes = append(ms.unsubscribes, Unsubscribe{ - CreatedAt: RFC2822Time(time.Now()), + ms.unsubscribes = append(ms.unsubscribes, mtypes.Unsubscribe{ + CreatedAt: mtypes.RFC2822Time(time.Now()), Tags: []string{"some", "tag"}, ID: "2", Address: "alice@example.com", @@ -58,20 +59,20 @@ func (ms *mockServer) listUnsubscribes(w http.ResponseWriter, r *http.Request) { } start, end := pageOffsets(idx, page, pivot, limit) var nextAddress, prevAddress string - var results []Unsubscribe + var results []mtypes.Unsubscribe if start != end { results = ms.unsubscribes[start:end] nextAddress = results[len(results)-1].Address prevAddress = results[0].Address } else { - results = []Unsubscribe{} + results = []mtypes.Unsubscribe{} nextAddress = pivot prevAddress = pivot } - toJSON(w, unsubscribesResponse{ - Paging: Paging{ + toJSON(w, mtypes.ListUnsubscribesResponse{ + Paging: mtypes.Paging{ First: getPageURL(r, url.Values{ "page": []string{"first"}, }), @@ -109,7 +110,7 @@ func (ms *mockServer) createUnsubscribe(w http.ResponseWriter, r *http.Request) defer ms.mutex.Unlock() ms.mutex.Lock() - var unsubscribes []Unsubscribe + var unsubscribes []mtypes.Unsubscribe if r.Header.Get("Content-Type") == "application/json" { body, err := ioutil.ReadAll(r.Body) if err != nil { @@ -140,7 +141,7 @@ func (ms *mockServer) createUnsubscribe(w http.ResponseWriter, r *http.Request) return } - unsubscribes = append(unsubscribes, Unsubscribe{Address: address, Tags: []string{tag}}) + unsubscribes = append(unsubscribes, mtypes.Unsubscribe{Address: address, Tags: []string{tag}}) } for _, unsubscribe := range unsubscribes { diff --git a/mock_validation.go b/mock_validation.go index 163180be..9a0a13c3 100644 --- a/mock_validation.go +++ b/mock_validation.go @@ -5,6 +5,7 @@ import ( "net/mail" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addValidationRoutes(r chi.Router) { @@ -18,7 +19,7 @@ func (ms *mockServer) validateEmailV4(w http.ResponseWriter, r *http.Request) { return } - var results ValidateEmailResponse + var results mtypes.ValidateEmailResponse results.Risk = "unknown" _, err := mail.ParseAddress(r.FormValue("address")) if err == nil { @@ -26,7 +27,7 @@ func (ms *mockServer) validateEmailV4(w http.ResponseWriter, r *http.Request) { } results.Reason = []string{"no-reason"} results.Result = "deliverable" - results.Engagement = &EngagementData{ + results.Engagement = &mtypes.EngagementData{ Engaging: false, Behavior: "disengaged", IsBot: false, diff --git a/mock_webhooks.go b/mock_webhooks.go index 163870bc..d6a5742c 100644 --- a/mock_webhooks.go +++ b/mock_webhooks.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/mailgun/mailgun-go/v4/mtypes" ) func (ms *mockServer) addWebhookRoutes(r chi.Router) { @@ -15,8 +16,8 @@ func (ms *mockServer) addWebhookRoutes(r chi.Router) { sr.Delete("/{webhook}", ms.deleteWebHook) }) - ms.webhooks = WebHooksListResponse{ - Webhooks: map[string]UrlOrUrls{ + ms.webhooks = mtypes.WebHooksListResponse{ + Webhooks: map[string]mtypes.UrlOrUrls{ "new-webhook": { Urls: []string{"http://example.com/new"}, }, @@ -38,8 +39,8 @@ func (ms *mockServer) getWebHook(w http.ResponseWriter, r *http.Request) { defer ms.mutex.Unlock() ms.mutex.Lock() - resp := WebHookResponse{ - Webhook: UrlOrUrls{ + resp := mtypes.WebHookResponse{ + Webhook: mtypes.UrlOrUrls{ Urls: ms.webhooks.Webhooks[chi.URLParam(r, "webhook")].Urls, }, } @@ -60,7 +61,7 @@ func (ms *mockServer) postWebHook(w http.ResponseWriter, r *http.Request) { for _, url := range r.Form["url"] { urls = append(urls, url) } - ms.webhooks.Webhooks[r.FormValue("id")] = UrlOrUrls{Urls: urls} + ms.webhooks.Webhooks[r.FormValue("id")] = mtypes.UrlOrUrls{Urls: urls} toJSON(w, okResp{Message: "success"}) } @@ -79,7 +80,7 @@ func (ms *mockServer) putWebHook(w http.ResponseWriter, r *http.Request) { for _, url := range r.Form["url"] { urls = append(urls, url) } - ms.webhooks.Webhooks[chi.URLParam(r, "webhook")] = UrlOrUrls{Urls: urls} + ms.webhooks.Webhooks[chi.URLParam(r, "webhook")] = mtypes.UrlOrUrls{Urls: urls} toJSON(w, okResp{Message: "success"}) } diff --git a/mtypes/analytics.go b/mtypes/analytics.go new file mode 100644 index 00000000..f2ac46db --- /dev/null +++ b/mtypes/analytics.go @@ -0,0 +1,14 @@ +package mtypes + +type MetricsPagination struct { + // Colon-separated value indicating column name and sort direction e.g. 'domain:asc'. + Sort string `json:"sort"` + // The number of items to skip over when satisfying the request. + // To get the first page of data set skip to zero. + // Then increment the skip by the limit for subsequent calls. + Skip int `json:"skip"` + // The maximum number of items returned in the response. + Limit int `json:"limit"` + // The total number of items in the query result set. + Total int `json:"total"` +} diff --git a/analytics_request.go b/mtypes/analytics_requests.go similarity index 97% rename from analytics_request.go rename to mtypes/analytics_requests.go index 470f30b8..650d216d 100644 --- a/analytics_request.go +++ b/mtypes/analytics_requests.go @@ -1,6 +1,6 @@ -package mailgun +package mtypes -type MetricsOptions struct { +type MetricsRequest struct { // A start date (default: 7 days before current time). Start RFC2822Time `json:"start,omitempty"` // An end date (default: current time). diff --git a/analytics_response.go b/mtypes/analytics_responses.go similarity index 99% rename from analytics_response.go rename to mtypes/analytics_responses.go index dab08001..20b9817b 100644 --- a/analytics_response.go +++ b/mtypes/analytics_responses.go @@ -1,4 +1,4 @@ -package mailgun +package mtypes type MetricsResponse struct { Start RFC2822Time `json:"start"` diff --git a/mtypes/bounces.go b/mtypes/bounces.go new file mode 100644 index 00000000..1f201781 --- /dev/null +++ b/mtypes/bounces.go @@ -0,0 +1,14 @@ +package mtypes + +// Bounce aggregates data relating to undeliverable messages to a specific intended recipient, +// identified by Address. +type Bounce struct { + // The time at which Mailgun detected the bounce. + CreatedAt RFC2822Time `json:"created_at"` + // Code provides the SMTP error code that caused the bounce + Code string `json:"code"` + // Address the bounce is for + Address string `json:"address"` + // human readable reason why + Error string `json:"error"` +} diff --git a/mtypes/credentials.go b/mtypes/credentials.go new file mode 100644 index 00000000..552915fa --- /dev/null +++ b/mtypes/credentials.go @@ -0,0 +1,8 @@ +package mtypes + +// A Credential structure describes a principle allowed to send or receive mail at the domain. +type Credential struct { + CreatedAt RFC2822Time `json:"created_at"` + Login string `json:"login"` + Password string `json:"password"` +} diff --git a/mtypes/doc.go b/mtypes/doc.go new file mode 100644 index 00000000..141da02d --- /dev/null +++ b/mtypes/doc.go @@ -0,0 +1,2 @@ +// Package mtypes contains API Mailgun requests, responses, and helper types. +package mtypes diff --git a/mtypes/domains.go b/mtypes/domains.go new file mode 100644 index 00000000..21557df4 --- /dev/null +++ b/mtypes/domains.go @@ -0,0 +1,51 @@ +package mtypes + +// Use these to specify a spam action when creating a new domain. +const ( + // SpamActionTag tags the received message with headers providing a measure of its spamness. + SpamActionTag = SpamAction("tag") + // SpamActionDisabled prevents Mailgun from taking any action on what it perceives to be spam. + SpamActionDisabled = SpamAction("disabled") + // SpamActionDelete instructs Mailgun to just block or delete the message all-together. + SpamActionDelete = SpamAction("delete") +) + +type SpamAction string + +// A Domain structure holds information about a domain used when sending mail. +type Domain struct { + CreatedAt RFC2822Time `json:"created_at"` + ID string `json:"id"` + IsDisabled bool `json:"is_disabled"` + Name string `json:"name"` + RequireTLS bool `json:"require_tls"` + SkipVerification bool `json:"skip_verification"` + SMTPLogin string `json:"smtp_login"` + SMTPPassword string `json:"smtp_password,omitempty"` + SpamAction SpamAction `json:"spam_action"` + State string `json:"state"` + Type string `json:"type"` + TrackingHost string `json:"tracking_host,omitempty"` + UseAutomaticSenderSecurity bool `json:"use_automatic_sender_security"` + WebPrefix string `json:"web_prefix"` + WebScheme string `json:"web_scheme"` + Wildcard bool `json:"wildcard"` +} + +// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun. +// Note that Mailgun does not host DNS records. +type DNSRecord struct { + Active bool `json:"is_active"` + Cached []string `json:"cached"` + Name string `json:"name,omitempty"` + Priority string `json:"priority,omitempty"` + RecordType string `json:"record_type"` + Valid string `json:"valid"` + Value string `json:"value"` +} + +type GetDomainResponse struct { + Domain Domain `json:"domain"` + ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"` + SendingDNSRecords []DNSRecord `json:"sending_dns_records"` +} diff --git a/mtypes/domains_connection.go b/mtypes/domains_connection.go new file mode 100644 index 00000000..e12ba8e3 --- /dev/null +++ b/mtypes/domains_connection.go @@ -0,0 +1,17 @@ +package mtypes + +type DomainConnectionResponse struct { + Connection DomainConnection `json:"connection"` +} + +type ListDomainsResponse struct { + // is -1 if Next() or First() have not been called + TotalCount int `json:"total_count"` + Items []Domain `json:"items"` +} + +// Specify the domain connection options +type DomainConnection struct { + RequireTLS bool `json:"require_tls"` + SkipVerification bool `json:"skip_verification"` +} diff --git a/mtypes/domains_tracking.go b/mtypes/domains_tracking.go new file mode 100644 index 00000000..d3258a0a --- /dev/null +++ b/mtypes/domains_tracking.go @@ -0,0 +1,19 @@ +package mtypes + +type DomainTrackingResponse struct { + Tracking DomainTracking `json:"tracking"` +} + +// Specify the domain tracking options +type DomainTracking struct { + Click TrackingStatus `json:"click"` + Open TrackingStatus `json:"open"` + Unsubscribe TrackingStatus `json:"unsubscribe"` +} + +// TrackingStatus is the tracking status of a domain +type TrackingStatus struct { + Active bool `json:"active"` + HTMLFooter string `json:"html_footer"` + TextFooter string `json:"text_footer"` +} diff --git a/mtypes/exports.go b/mtypes/exports.go new file mode 100644 index 00000000..2227d8c4 --- /dev/null +++ b/mtypes/exports.go @@ -0,0 +1,11 @@ +package mtypes + +type ExportList struct { + Items []Export `json:"items"` +} + +type Export struct { + ID string `json:"id"` + Status string `json:"status"` + URL string `json:"url"` +} diff --git a/mtypes/ips.go b/mtypes/ips.go new file mode 100644 index 00000000..04f8f3b1 --- /dev/null +++ b/mtypes/ips.go @@ -0,0 +1,12 @@ +package mtypes + +type IPAddressListResponse struct { + TotalCount int `json:"total_count"` + Items []string `json:"items"` +} + +type IPAddress struct { + IP string `json:"ip"` + RDNS string `json:"rdns"` + Dedicated bool `json:"dedicated"` +} diff --git a/mtypes/limits.go b/mtypes/limits.go new file mode 100644 index 00000000..f2ec9115 --- /dev/null +++ b/mtypes/limits.go @@ -0,0 +1,6 @@ +package mtypes + +type TagLimits struct { + Limit int `json:"limit"` + Count int `json:"count"` +} diff --git a/mtypes/mailgun.go b/mtypes/mailgun.go new file mode 100644 index 00000000..21029f24 --- /dev/null +++ b/mtypes/mailgun.go @@ -0,0 +1,5 @@ +package mtypes + +func ptr[T any](v T) *T { + return &v +} diff --git a/mtypes/mailing_lists.go b/mtypes/mailing_lists.go new file mode 100644 index 00000000..5081fd14 --- /dev/null +++ b/mtypes/mailing_lists.go @@ -0,0 +1,51 @@ +package mtypes + +// Specify the access of a mailing list member +type AccessLevel string + +// A mailing list may have one of three membership modes. +const ( + // ReadOnly specifies that nobody, including Members, may send messages to + // the mailing list. Messages distributed on such lists come from list + // administrator accounts only. + AccessLevelReadOnly = "readonly" + // Members specifies that only those who subscribe to the mailing list may + // send messages. + AccessLevelMembers = "members" + // Everyone specifies that anyone and everyone may both read and submit + // messages to the mailing list, including non-subscribers. + AccessLevelEveryone = "everyone" +) + +// Set where replies should go +type ReplyPreference string + +// Replies to a mailing list should go to one of two preferred destinations. +const ( + // List specifies that replies should be sent to the mailing list address. + ReplyPreferenceList = "list" + // Sender specifies that replies should be sent to the sender (FROM) address. + ReplyPreferenceSender = "sender" +) + +// A List structure provides information for a mailing list. +// +// AccessLevel may be one of ReadOnly, Members, or Everyone. +type MailingList struct { + Address string `json:"address,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AccessLevel AccessLevel `json:"access_level,omitempty"` + ReplyPreference ReplyPreference `json:"reply_preference,omitempty"` + CreatedAt RFC2822Time `json:"created_at,omitempty"` + MembersCount int `json:"members_count,omitempty"` +} + +type ListMailingListsResponse struct { + Items []MailingList `json:"items"` + Paging Paging `json:"paging"` +} + +type GetMailingListResponse struct { + MailingList MailingList `json:"list"` +} diff --git a/mtypes/members.go b/mtypes/members.go new file mode 100644 index 00000000..381ef7b3 --- /dev/null +++ b/mtypes/members.go @@ -0,0 +1,31 @@ +package mtypes + +// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not. +// This attribute may be used to filter the results returned by GetSubscribers(). +// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search. +// We use a pointer to boolean as a kind of trinary data type: +// if nil, the relevant data type remains unspecified. +// Otherwise, its value is either true or false. +var ( + All *bool + Subscribed = ptr(true) + Unsubscribed = ptr(false) +) + +// A Member structure represents a member of the mailing list. +// The Vars field can represent any JSON-encodable data. +type Member struct { + Address string `json:"address,omitempty"` + Name string `json:"name,omitempty"` + Subscribed *bool `json:"subscribed,omitempty"` + Vars map[string]any `json:"vars,omitempty"` +} + +type MemberListResponse struct { + Lists []Member `json:"items"` + Paging Paging `json:"paging"` +} + +type MemberResponse struct { + Member Member `json:"member"` +} diff --git a/mtypes/paging.go b/mtypes/paging.go new file mode 100644 index 00000000..3d7fa53e --- /dev/null +++ b/mtypes/paging.go @@ -0,0 +1,8 @@ +package mtypes + +type Paging struct { + First string `json:"first,omitempty"` + Next string `json:"next,omitempty"` + Previous string `json:"previous,omitempty"` + Last string `json:"last,omitempty"` +} diff --git a/reporting.go b/mtypes/resolution.go similarity index 72% rename from reporting.go rename to mtypes/resolution.go index f9813839..482e0aa5 100644 --- a/reporting.go +++ b/mtypes/resolution.go @@ -1,6 +1,4 @@ -package mailgun - -// common things for stats and metrics +package mtypes type Resolution string diff --git a/rfc2822.go b/mtypes/rfc2822.go similarity index 98% rename from rfc2822.go rename to mtypes/rfc2822.go index 6ff26fdd..d28eab2f 100644 --- a/rfc2822.go +++ b/mtypes/rfc2822.go @@ -1,4 +1,4 @@ -package mailgun +package mtypes import ( "strconv" diff --git a/rfc2822_test.go b/mtypes/rfc2822_test.go similarity index 98% rename from rfc2822_test.go rename to mtypes/rfc2822_test.go index 770b3485..250da528 100644 --- a/rfc2822_test.go +++ b/mtypes/rfc2822_test.go @@ -1,4 +1,4 @@ -package mailgun +package mtypes import ( "encoding/json" diff --git a/mtypes/routes.go b/mtypes/routes.go new file mode 100644 index 00000000..eada95a9 --- /dev/null +++ b/mtypes/routes.go @@ -0,0 +1,35 @@ +package mtypes + +// A Route structure contains information on a configured or to-be-configured route. +// When creating a new route, the SDK only uses a subset of the fields of this structure. +// In particular, CreatedAt and ID are meaningless in this context, and will be ignored. +// Only Priority, Description, Expression, and Actions need be provided. +type Route struct { + // The Priority field indicates how soon the route works relative to other configured routes. + // Routes of equal priority are consulted in chronological order. + Priority int `json:"priority,omitempty"` + // The Description field provides a human-readable description for the route. + // Mailgun ignores this field except to provide the description when viewing the Mailgun web control panel. + Description string `json:"description,omitempty"` + // The Expression field lets you specify a pattern to match incoming messages against. + Expression string `json:"expression,omitempty"` + // The Actions field contains strings specifying what to do + // with any message which matches the provided expression. + Actions []string `json:"actions,omitempty"` + + // The CreatedAt field provides a time-stamp for when the route came into existence. + CreatedAt RFC2822Time `json:"created_at,omitempty"` + // ID field provides a unique identifier for this route. + Id string `json:"id,omitempty"` +} + +type RoutesListResponse struct { + // is -1 if Next() or First() have not been called + TotalCount int `json:"total_count"` + Items []Route `json:"items"` +} + +type CreateRouteResp struct { + Message string `json:"message"` + Route `json:"route"` +} diff --git a/mtypes/spam_complaints.go b/mtypes/spam_complaints.go new file mode 100644 index 00000000..2429d842 --- /dev/null +++ b/mtypes/spam_complaints.go @@ -0,0 +1,14 @@ +package mtypes + +// Complaint structures track how many times one of your emails have been marked as spam. +// the recipient thought your messages were not solicited. +type Complaint struct { + Count int `json:"count"` + CreatedAt RFC2822Time `json:"created_at"` + Address string `json:"address"` +} + +type ComplaintsResponse struct { + Paging Paging `json:"paging"` + Items []Complaint `json:"items"` +} diff --git a/mtypes/stored_messages.go b/mtypes/stored_messages.go new file mode 100644 index 00000000..6f251051 --- /dev/null +++ b/mtypes/stored_messages.go @@ -0,0 +1,45 @@ +package mtypes + +// StoredMessage structures contain the (parsed) message content for an email +// sent to a Mailgun account. +// +// The MessageHeaders field is special, in that it's formatted as a slice of pairs. +// Each pair consists of a name [0] and value [1]. Array notation is used instead of a map +// because that's how it's sent over the wire, and it's how encoding/json expects this field +// to be. +type StoredMessage struct { + Recipients string `json:"recipients"` + Sender string `json:"sender"` + From string `json:"from"` + Subject string `json:"subject"` + BodyPlain string `json:"body-plain"` + StrippedText string `json:"stripped-text"` + StrippedSignature string `json:"stripped-signature"` + BodyHtml string `json:"body-html"` + StrippedHtml string `json:"stripped-html"` + Attachments []StoredAttachment `json:"attachments"` + MessageUrl string `json:"message-url"` + ContentIDMap map[string]struct { + URL string `json:"url"` + ContentType string `json:"content-type"` + Name string `json:"name"` + Size int64 `json:"size"` + } `json:"content-id-map"` + MessageHeaders [][]string `json:"message-headers"` +} + +// StoredAttachment structures contain information on an attachment associated with a stored message. +type StoredAttachment struct { + Size int `json:"size"` + URL string `json:"url"` + Name string `json:"name"` + ContentType string `json:"content-type"` +} + +type StoredMessageRaw struct { + Recipients string `json:"recipients"` + Sender string `json:"sender"` + From string `json:"from"` + Subject string `json:"subject"` + BodyMime string `json:"body-mime"` +} diff --git a/mtypes/subaccounts.go b/mtypes/subaccounts.go new file mode 100644 index 00000000..a2409190 --- /dev/null +++ b/mtypes/subaccounts.go @@ -0,0 +1,17 @@ +package mtypes + +// A Subaccount structure holds information about a subaccount. +type Subaccount struct { + ID string `json:"id"` + Name string `json:"name"` + Status string `json:"status"` +} + +type SubaccountResponse struct { + Item Subaccount `json:"subaccount"` +} + +type ListSubaccountsResponse struct { + Items []Subaccount `json:"subaccounts"` + Total int `json:"total"` +} diff --git a/mtypes/tags.go b/mtypes/tags.go new file mode 100644 index 00000000..fd374884 --- /dev/null +++ b/mtypes/tags.go @@ -0,0 +1,17 @@ +package mtypes + +import ( + "time" +) + +type Tag struct { + Value string `json:"tag"` + Description string `json:"description"` + FirstSeen *time.Time `json:"first-seen,omitempty"` + LastSeen *time.Time `json:"last-seen,omitempty"` +} + +type TagsResponse struct { + Items []Tag `json:"items"` + Paging Paging `json:"paging"` +} diff --git a/mtypes/template.go b/mtypes/template.go new file mode 100644 index 00000000..aba326fb --- /dev/null +++ b/mtypes/template.go @@ -0,0 +1,26 @@ +package mtypes + +type TemplateEngine string + +// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine +const ( + TemplateEngineHandlebars = TemplateEngine("handlebars") + TemplateEngineGo = TemplateEngine("go") +) + +type Template struct { + Name string `json:"name"` + Description string `json:"description"` + CreatedAt RFC2822Time `json:"createdAt"` + Version TemplateVersion `json:"version,omitempty"` +} + +type TemplateResp struct { + Item Template `json:"template"` + Message string `json:"message"` +} + +type ListTemplateResp struct { + Items []Template `json:"items"` + Paging Paging `json:"paging"` +} diff --git a/mtypes/template_versions.go b/mtypes/template_versions.go new file mode 100644 index 00000000..fdc2a091 --- /dev/null +++ b/mtypes/template_versions.go @@ -0,0 +1,18 @@ +package mtypes + +type TemplateVersion struct { + Tag string `json:"tag"` + Template string `json:"template,omitempty"` + Engine TemplateEngine `json:"engine"` + CreatedAt RFC2822Time `json:"createdAt"` + Comment string `json:"comment"` + Active bool `json:"active"` +} + +type TemplateVersionListResp struct { + Template struct { + Template + Versions []TemplateVersion `json:"versions,omitempty"` + } `json:"template"` + Paging Paging `json:"paging"` +} diff --git a/mtypes/unsubscribes.go b/mtypes/unsubscribes.go new file mode 100644 index 00000000..5e6dd153 --- /dev/null +++ b/mtypes/unsubscribes.go @@ -0,0 +1,13 @@ +package mtypes + +type Unsubscribe struct { + CreatedAt RFC2822Time `json:"created_at,omitempty"` + Tags []string `json:"tags,omitempty"` + ID string `json:"id,omitempty"` + Address string `json:"address"` +} + +type ListUnsubscribesResponse struct { + Paging Paging `json:"paging"` + Items []Unsubscribe `json:"items"` +} diff --git a/mtypes/validation.go b/mtypes/validation.go new file mode 100644 index 00000000..fb4c2f54 --- /dev/null +++ b/mtypes/validation.go @@ -0,0 +1,43 @@ +package mtypes + +// ValidateEmailResponse records basic facts about a validated e-mail address. +// See the ValidateEmail method and example for more details. +type ValidateEmailResponse struct { + // Echoes the address provided. + Address string `json:"address"` + + // Indicates whether Mailgun thinks the address is from a known + // disposable mailbox provider. + IsDisposableAddress bool `json:"is_disposable_address"` + + // Indicates whether Mailgun thinks the address is an email distribution list. + IsRoleAddress bool `json:"is_role_address"` + + // A list of potential reasons why a specific validation may be unsuccessful. (Available in the v4 response) + Reason []string `json:"reason"` + + // Result + Result string `json:"result"` + + // Risk assessment for the provided email. + Risk string `json:"risk"` + + LastSeen int64 `json:"last_seen,omitempty"` + + // Provides a simple recommendation in case the address is invalid or + // Mailgun thinks you might have a typo. May be empty, in which case + // Mailgun has no recommendation to give. + DidYouMean string `json:"did_you_mean,omitempty"` + + // Engagement results are a macro-level view that explain an email recipient’s propensity to engage. + // https://documentation.mailgun.com/docs/inboxready/mailgun-validate/validate_engagement/ + Engagement *EngagementData `json:"engagement,omitempty"` + + RootAddress string `json:"root_address,omitempty"` +} + +type EngagementData struct { + Engaging bool `json:"engaging"` + IsBot bool `json:"is_bot"` + Behavior string `json:"behavior,omitempty"` +} diff --git a/mtypes/webhooks.go b/mtypes/webhooks.go new file mode 100644 index 00000000..d1c76426 --- /dev/null +++ b/mtypes/webhooks.go @@ -0,0 +1,31 @@ +package mtypes + +import ( + "github.com/mailgun/mailgun-go/v4/events" +) + +type UrlOrUrls struct { + Urls []string `json:"urls"` + Url string `json:"url"` +} + +type WebHooksListResponse struct { + Webhooks map[string]UrlOrUrls `json:"webhooks"` +} + +type WebHookResponse struct { + Webhook UrlOrUrls `json:"webhook"` +} + +// Signature represents the signature portion of the webhook POST body +type Signature struct { + TimeStamp string `json:"timestamp"` + Token string `json:"token"` + Signature string `json:"signature"` +} + +// WebhookPayload represents the JSON payload provided when a Webhook is called by mailgun +type WebhookPayload struct { + Signature Signature `json:"signature"` + EventData events.RawJSON `json:"event-data"` +} diff --git a/parse.go b/parse.go index 6d899947..09c8201e 100644 --- a/parse.go +++ b/parse.go @@ -1,5 +1,7 @@ package mailgun +// TODO(v5): move to events package? + import ( "fmt" "reflect" diff --git a/routes.go b/routes.go index 4693f6e3..677eb23f 100644 --- a/routes.go +++ b/routes.go @@ -6,30 +6,9 @@ import ( "net/url" "strconv" "time" -) -// A Route structure contains information on a configured or to-be-configured route. -// When creating a new route, the SDK only uses a subset of the fields of this structure. -// In particular, CreatedAt and ID are meaningless in this context, and will be ignored. -// Only Priority, Description, Expression, and Actions need be provided. -type Route struct { - // The Priority field indicates how soon the route works relative to other configured routes. - // Routes of equal priority are consulted in chronological order. - Priority int `json:"priority,omitempty"` - // The Description field provides a human-readable description for the route. - // Mailgun ignores this field except to provide the description when viewing the Mailgun web control panel. - Description string `json:"description,omitempty"` - // The Expression field lets you specify a pattern to match incoming messages against. - Expression string `json:"expression,omitempty"` - // The Actions field contains strings specifying what to do - // with any message which matches the provided expression. - Actions []string `json:"actions,omitempty"` - - // The CreatedAt field provides a time-stamp for when the route came into existence. - CreatedAt RFC2822Time `json:"created_at,omitempty"` - // ID field provides a unique identifier for this route. - Id string `json:"id,omitempty"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // ForwardedMessage represents the payload the server will get on match // You can use ExtractForwardRoute() to extract PostForm into the struct, or you can use only the struct and parse the form manually @@ -94,17 +73,6 @@ func ExtractForwardedMessage(formValues url.Values) ForwardedMessage { return forwardedMessage } -type routesListResponse struct { - // is -1 if Next() or First() have not been called - TotalCount int `json:"total_count"` - Items []Route `json:"items"` -} - -type createRouteResp struct { - Message string `json:"message"` - Route `json:"route"` -} - // ListRoutes allows you to iterate through a list of routes returned by the API func (mg *MailgunImpl) ListRoutes(opts *ListOptions) *RoutesIterator { var limit int @@ -119,13 +87,13 @@ func (mg *MailgunImpl) ListRoutes(opts *ListOptions) *RoutesIterator { return &RoutesIterator{ mg: mg, url: generateApiUrl(mg, 3, routesEndpoint), - routesListResponse: routesListResponse{TotalCount: -1}, + RoutesListResponse: mtypes.RoutesListResponse{TotalCount: -1}, limit: limit, } } type RoutesIterator struct { - routesListResponse + mtypes.RoutesListResponse limit int mg Mailgun @@ -147,7 +115,7 @@ func (ri *RoutesIterator) Offset() int { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool { +func (ri *RoutesIterator) Next(ctx context.Context, items *[]mtypes.Route) bool { if ri.err != nil { return false } @@ -157,7 +125,7 @@ func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool { return false } - cpy := make([]Route, len(ri.Items)) + cpy := make([]mtypes.Route, len(ri.Items)) copy(cpy, ri.Items) *items = cpy if len(ri.Items) == 0 { @@ -170,7 +138,7 @@ func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool { +func (ri *RoutesIterator) First(ctx context.Context, items *[]mtypes.Route) bool { if ri.err != nil { return false } @@ -178,7 +146,7 @@ func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool { if ri.err != nil { return false } - cpy := make([]Route, len(ri.Items)) + cpy := make([]mtypes.Route, len(ri.Items)) copy(cpy, ri.Items) *items = cpy ri.offset = len(ri.Items) @@ -189,7 +157,7 @@ func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool { // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool { +func (ri *RoutesIterator) Last(ctx context.Context, items *[]mtypes.Route) bool { if ri.err != nil { return false } @@ -207,7 +175,7 @@ func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool { if ri.err != nil { return false } - cpy := make([]Route, len(ri.Items)) + cpy := make([]mtypes.Route, len(ri.Items)) copy(cpy, ri.Items) *items = cpy return true @@ -216,7 +184,7 @@ func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool { +func (ri *RoutesIterator) Previous(ctx context.Context, items *[]mtypes.Route) bool { if ri.err != nil { return false } @@ -234,7 +202,7 @@ func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool { if ri.err != nil { return false } - cpy := make([]Route, len(ri.Items)) + cpy := make([]mtypes.Route, len(ri.Items)) copy(cpy, ri.Items) *items = cpy @@ -254,14 +222,14 @@ func (ri *RoutesIterator) fetch(ctx context.Context, skip, limit int) error { r.addParameter("limit", strconv.Itoa(limit)) } - return getResponseFromJSON(ctx, r, &ri.routesListResponse) + return getResponseFromJSON(ctx, r, &ri.RoutesListResponse) } // CreateRoute installs a new route for your domain. // The route structure you provide serves as a template, and // only a subset of the fields influence the operation. // See the Route structure definition for more details. -func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype Route) (_ignored Route, err error) { +func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype mtypes.Route) (_ignored mtypes.Route, err error) { r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -272,7 +240,7 @@ func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype Route) (_ignor for _, action := range prototype.Actions { p.addValue("action", action) } - var resp createRouteResp + var resp mtypes.CreateRouteResp if err := postResponseFromJSON(ctx, r, p, &resp); err != nil { return _ignored, err } @@ -292,17 +260,17 @@ func (mg *MailgunImpl) DeleteRoute(ctx context.Context, id string) error { } // GetRoute retrieves the complete route definition associated with the unique route ID. -func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (Route, error) { +func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (mtypes.Route, error) { r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint) + "/" + id) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) var envelope struct { - Message string `json:"message"` - *Route `json:"route"` + Message string `json:"message"` + *mtypes.Route `json:"route"` } err := getResponseFromJSON(ctx, r, &envelope) if err != nil { - return Route{}, err + return mtypes.Route{}, err } return *envelope.Route, err @@ -311,7 +279,7 @@ func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (Route, error) { // UpdateRoute provides an "in-place" update of the specified route. // Only those route fields which are non-zero or non-empty are updated. // All other fields remain as-is. -func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route Route) (Route, error) { +func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route mtypes.Route) (mtypes.Route, error) { r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint) + "/" + id) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -332,7 +300,7 @@ func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route Route) } // For some reason, this API function just returns a bare Route on success. // Unsure why this is the case; it seems like it ought to be a bug. - var envelope Route + var envelope mtypes.Route err := putResponseFromJSON(ctx, r, p, &envelope) return envelope, err } diff --git a/routes_test.go b/routes_test.go index 7b8a20a9..0410071a 100644 --- a/routes_test.go +++ b/routes_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -17,7 +18,7 @@ func TestRouteCRUD(t *testing.T) { ctx := context.Background() var countRoutes = func() int { it := mg.ListRoutes(nil) - var page []mailgun.Route + var page []mtypes.Route it.Next(ctx, &page) require.NoError(t, it.Err()) return it.TotalCount @@ -25,7 +26,7 @@ func TestRouteCRUD(t *testing.T) { routeCount := countRoutes() - newRoute, err := mg.CreateRoute(ctx, mailgun.Route{ + newRoute, err := mg.CreateRoute(ctx, mtypes.Route{ Priority: 1, Description: "Sample Route", Expression: "match_recipient(\".*@samples.mailgun.org\")", @@ -50,7 +51,7 @@ func TestRouteCRUD(t *testing.T) { require.NoError(t, err) assert.Equal(t, newRoute, theRoute) - changedRoute, err := mg.UpdateRoute(ctx, newRoute.Id, mailgun.Route{ + changedRoute, err := mg.UpdateRoute(ctx, newRoute.Id, mtypes.Route{ Priority: 2, }) require.NoError(t, err) @@ -65,7 +66,7 @@ func TestRoutesIterator(t *testing.T) { it := mg.ListRoutes(&mailgun.ListOptions{Limit: 2}) - var firstPage, secondPage, previousPage, lastPage []mailgun.Route + var firstPage, secondPage, previousPage, lastPage []mtypes.Route var ctx = context.Background() // Calling Last() is invalid unless you first use First() or Next() diff --git a/spam_complaints.go b/spam_complaints.go index dcaf50b1..0a8802b7 100644 --- a/spam_complaints.go +++ b/spam_complaints.go @@ -3,25 +3,14 @@ package mailgun import ( "context" "strconv" + + "github.com/mailgun/mailgun-go/v4/mtypes" ) const ( complaintsEndpoint = "complaints" ) -// Complaint structures track how many times one of your emails have been marked as spam. -// the recipient thought your messages were not solicited. -type Complaint struct { - Count int `json:"count"` - CreatedAt RFC2822Time `json:"created_at"` - Address string `json:"address"` -} - -type complaintsResponse struct { - Paging Paging `json:"paging"` - Items []Complaint `json:"items"` -} - // ListComplaints returns a set of spam complaints registered against your domain. // Recipients of your messages can click on a link which sends feedback to Mailgun // indicating that the message they received is, to them, spam. @@ -37,13 +26,13 @@ func (mg *MailgunImpl) ListComplaints(domain string, opts *ListOptions) *Complai url, err := r.generateUrlWithParameters() return &ComplaintsIterator{ mg: mg, - complaintsResponse: complaintsResponse{Paging: Paging{Next: url, First: url}}, + ComplaintsResponse: mtypes.ComplaintsResponse{Paging: mtypes.Paging{Next: url, First: url}}, err: err, } } type ComplaintsIterator struct { - complaintsResponse + mtypes.ComplaintsResponse mg Mailgun err error } @@ -56,7 +45,7 @@ func (ci *ComplaintsIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool { +func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]mtypes.Complaint) bool { if ci.err != nil { return false } @@ -64,7 +53,7 @@ func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool if ci.err != nil { return false } - cpy := make([]Complaint, len(ci.Items)) + cpy := make([]mtypes.Complaint, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -74,7 +63,7 @@ func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) bool { +func (ci *ComplaintsIterator) First(ctx context.Context, items *[]mtypes.Complaint) bool { if ci.err != nil { return false } @@ -82,7 +71,7 @@ func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) boo if ci.err != nil { return false } - cpy := make([]Complaint, len(ci.Items)) + cpy := make([]mtypes.Complaint, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -92,7 +81,7 @@ func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) boo // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool { +func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]mtypes.Complaint) bool { if ci.err != nil { return false } @@ -100,7 +89,7 @@ func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool if ci.err != nil { return false } - cpy := make([]Complaint, len(ci.Items)) + cpy := make([]mtypes.Complaint, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -109,7 +98,7 @@ func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]Complaint) bool { +func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]mtypes.Complaint) bool { if ci.err != nil { return false } @@ -120,7 +109,7 @@ func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]Complaint) if ci.err != nil { return false } - cpy := make([]Complaint, len(ci.Items)) + cpy := make([]mtypes.Complaint, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -133,17 +122,17 @@ func (ci *ComplaintsIterator) fetch(ctx context.Context, url string) error { r.setClient(ci.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, ci.mg.APIKey()) - return getResponseFromJSON(ctx, r, &ci.complaintsResponse) + return getResponseFromJSON(ctx, r, &ci.ComplaintsResponse) } // GetComplaint returns a single complaint record filed by a recipient at the email address provided. // If no complaint exists, the Complaint instance returned will be empty. -func (mg *MailgunImpl) GetComplaint(ctx context.Context, domain, address string) (Complaint, error) { +func (mg *MailgunImpl) GetComplaint(ctx context.Context, domain, address string) (mtypes.Complaint, error) { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, complaintsEndpoint, domain) + "/" + address) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var c Complaint + var c mtypes.Complaint err := getResponseFromJSON(ctx, r, &c) return c, err } diff --git a/spam_complaints_test.go b/spam_complaints_test.go index d0edac59..11b9d0ee 100644 --- a/spam_complaints_test.go +++ b/spam_complaints_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/require" ) @@ -18,7 +19,7 @@ func TestGetComplaints(t *testing.T) { ctx := context.Background() it := mg.ListComplaints(testDomain, nil) - var page []mailgun.Complaint + var page []mtypes.Complaint for it.Next(ctx, &page) { } require.NoError(t, it.Err()) @@ -51,7 +52,7 @@ func TestCreateDeleteComplaint(t *testing.T) { it := mg.ListComplaints(testDomain, nil) require.NoError(t, it.Err()) - var page []mailgun.Complaint + var page []mtypes.Complaint for it.Next(ctx, &page) { for _, complaint := range page { t.Logf("Complaint Address: %s\n", complaint.Address) @@ -84,7 +85,7 @@ func TestCreateDeleteComplaintList(t *testing.T) { it := mg.ListComplaints(testDomain, nil) require.NoError(t, it.Err()) - var page []mailgun.Complaint + var page []mtypes.Complaint for it.Next(ctx, &page) { for _, complaint := range page { t.Logf("Complaint Address: %s\n", complaint.Address) diff --git a/storage_test.go b/storage_test.go index 249671be..bcf0f757 100644 --- a/storage_test.go +++ b/storage_test.go @@ -23,7 +23,7 @@ func TestStorage(t *testing.T) { msg, id, err := mg.Send(ctx, m) require.NoError(t, err) - t.Logf("New Email: %s Id: %s\n", msg, id) + t.Logf("New Email: %s ID: %s\n", msg, id) url, err := findStoredMessageURL(mg, strings.Trim(id, "<>")) require.NoError(t, err) diff --git a/stored_messages.go b/stored_messages.go index 46be347f..588c2997 100644 --- a/stored_messages.go +++ b/stored_messages.go @@ -3,65 +3,24 @@ package mailgun import ( "context" "errors" -) - -// StoredMessage structures contain the (parsed) message content for an email -// sent to a Mailgun account. -// -// The MessageHeaders field is special, in that it's formatted as a slice of pairs. -// Each pair consists of a name [0] and value [1]. Array notation is used instead of a map -// because that's how it's sent over the wire, and it's how encoding/json expects this field -// to be. -type StoredMessage struct { - Recipients string `json:"recipients"` - Sender string `json:"sender"` - From string `json:"from"` - Subject string `json:"subject"` - BodyPlain string `json:"body-plain"` - StrippedText string `json:"stripped-text"` - StrippedSignature string `json:"stripped-signature"` - BodyHtml string `json:"body-html"` - StrippedHtml string `json:"stripped-html"` - Attachments []StoredAttachment `json:"attachments"` - MessageUrl string `json:"message-url"` - ContentIDMap map[string]struct { - Url string `json:"url"` - ContentType string `json:"content-type"` - Name string `json:"name"` - Size int64 `json:"size"` - } `json:"content-id-map"` - MessageHeaders [][]string `json:"message-headers"` -} -// StoredAttachment structures contain information on an attachment associated with a stored message. -type StoredAttachment struct { - Size int `json:"size"` - Url string `json:"url"` - Name string `json:"name"` - ContentType string `json:"content-type"` -} - -type StoredMessageRaw struct { - Recipients string `json:"recipients"` - Sender string `json:"sender"` - From string `json:"from"` - Subject string `json:"subject"` - BodyMime string `json:"body-mime"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // GetStoredMessage retrieves information about a received e-mail message. // This provides visibility into, e.g., replies to a message sent to a mailing list. -func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, url string) (StoredMessage, error) { +func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, url string) (mtypes.StoredMessage, error) { r := newHTTPRequest(url) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var response StoredMessage + var response mtypes.StoredMessage err := getResponseFromJSON(ctx, r, &response) return response, err } -// Given a storage id resend the stored message to the specified recipients +// ReSend given a storage id resend the stored message to the specified recipients +// TODO(v5): return SendMessageResponse func (mg *MailgunImpl) ReSend(ctx context.Context, url string, recipients ...string) (msg, id string, err error) { r := newHTTPRequest(url) r.setClient(mg.HTTPClient()) @@ -83,19 +42,19 @@ func (mg *MailgunImpl) ReSend(ctx context.Context, url string, recipients ...str return "", "", err } - return resp.Message, resp.Id, nil + return resp.Message, resp.ID, nil } // GetStoredMessageRaw retrieves the raw MIME body of a received e-mail message. // Compared to GetStoredMessage, it gives access to the unparsed MIME body, and // thus delegates to the caller the required parsing. -func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, url string) (StoredMessageRaw, error) { +func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, url string) (mtypes.StoredMessageRaw, error) { r := newHTTPRequest(url) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) r.addHeader("Accept", "message/rfc2822") - var response StoredMessageRaw + var response mtypes.StoredMessageRaw err := getResponseFromJSON(ctx, r, &response) return response, err } diff --git a/subaccounts.go b/subaccounts.go index 6a45dd44..62fd304d 100644 --- a/subaccounts.go +++ b/subaccounts.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "strconv" + + "github.com/mailgun/mailgun-go/v4/mtypes" ) type ListSubaccountsOptions struct { @@ -14,7 +16,7 @@ type ListSubaccountsOptions struct { } type SubaccountsIterator struct { - subaccountsListResponse + mtypes.ListSubaccountsResponse mg Mailgun limit int @@ -26,22 +28,6 @@ type SubaccountsIterator struct { err error } -// A Subaccount structure holds information about a subaccount. -type Subaccount struct { - Id string `json:"id"` - Name string `json:"name"` - Status string `json:"status"` -} - -type SubaccountResponse struct { - Item Subaccount `json:"subaccount"` -} - -type subaccountsListResponse struct { - Items []Subaccount `json:"subaccounts"` - Total int `json:"total"` -} - // ListSubaccounts retrieves a set of subaccount linked to the primary Mailgun account. func (mg *MailgunImpl) ListSubaccounts(opts *ListSubaccountsOptions) *SubaccountsIterator { r := newHTTPRequest(generateSubaccountsApiUrl(mg)) @@ -64,7 +50,7 @@ func (mg *MailgunImpl) ListSubaccounts(opts *ListSubaccountsOptions) *Subaccount return &SubaccountsIterator{ mg: mg, url: generateSubaccountsApiUrl(mg), - subaccountsListResponse: subaccountsListResponse{Total: -1}, + ListSubaccountsResponse: mtypes.ListSubaccountsResponse{Total: -1}, limit: limit, skip: skip, sortArray: sortArray, @@ -85,7 +71,7 @@ func (ri *SubaccountsIterator) Offset() int { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bool { +func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]mtypes.Subaccount) bool { if ri.err != nil { return false } @@ -95,7 +81,7 @@ func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bo return false } - cpy := make([]Subaccount, len(ri.Items)) + cpy := make([]mtypes.Subaccount, len(ri.Items)) copy(cpy, ri.Items) *items = cpy if len(ri.Items) == 0 { @@ -108,7 +94,7 @@ func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bo // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) bool { +func (ri *SubaccountsIterator) First(ctx context.Context, items *[]mtypes.Subaccount) bool { if ri.err != nil { return false } @@ -116,7 +102,7 @@ func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) b if ri.err != nil { return false } - cpy := make([]Subaccount, len(ri.Items)) + cpy := make([]mtypes.Subaccount, len(ri.Items)) copy(cpy, ri.Items) *items = cpy ri.offset = len(ri.Items) @@ -127,7 +113,7 @@ func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) b // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bool { +func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]mtypes.Subaccount) bool { if ri.err != nil { return false } @@ -145,7 +131,7 @@ func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bo if ri.err != nil { return false } - cpy := make([]Subaccount, len(ri.Items)) + cpy := make([]mtypes.Subaccount, len(ri.Items)) copy(cpy, ri.Items) *items = cpy return true @@ -154,7 +140,7 @@ func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bo // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]Subaccount) bool { +func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]mtypes.Subaccount) bool { if ri.err != nil { return false } @@ -172,7 +158,7 @@ func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]Subaccount if ri.err != nil { return false } - cpy := make([]Subaccount, len(ri.Items)) + cpy := make([]mtypes.Subaccount, len(ri.Items)) copy(cpy, ri.Items) *items = cpy @@ -192,54 +178,54 @@ func (ri *SubaccountsIterator) fetch(ctx context.Context, skip, limit int) error r.addParameter("limit", strconv.Itoa(limit)) } - return getResponseFromJSON(ctx, r, &ri.subaccountsListResponse) + return getResponseFromJSON(ctx, r, &ri.ListSubaccountsResponse) } // CreateSubaccount instructs Mailgun to create a new account (Subaccount) that is linked to the primary account. // Subaccounts are child accounts that share the same plan and usage allocations as the primary, but have their own // assets (sending domains, unique users, API key, SMTP credentials, settings, statistics and site login). // All you need is the name of the subaccount. -func (mg *MailgunImpl) CreateSubaccount(ctx context.Context, subaccountName string) (SubaccountResponse, error) { +func (mg *MailgunImpl) CreateSubaccount(ctx context.Context, subaccountName string) (mtypes.SubaccountResponse, error) { r := newHTTPRequest(generateSubaccountsApiUrl(mg)) r.setClient(mg.client) r.setBasicAuth(basicAuthUser, mg.APIKey()) payload := newUrlEncodedPayload() payload.addValue("name", subaccountName) - resp := SubaccountResponse{} + resp := mtypes.SubaccountResponse{} err := postResponseFromJSON(ctx, r, payload, &resp) return resp, err } // GetSubaccount retrieves detailed information about subaccount using subaccountID. -func (mg *MailgunImpl) GetSubaccount(ctx context.Context, subaccountID string) (SubaccountResponse, error) { +func (mg *MailgunImpl) GetSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error) { r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountID) r.setClient(mg.client) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp SubaccountResponse + var resp mtypes.SubaccountResponse err := getResponseFromJSON(ctx, r, &resp) return resp, err } // EnableSubaccount instructs Mailgun to enable subaccount. -func (mg *MailgunImpl) EnableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) { +func (mg *MailgunImpl) EnableSubaccount(ctx context.Context, subaccountId string) (mtypes.SubaccountResponse, error) { r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountId + "/" + "enable") r.setClient(mg.client) r.setBasicAuth(basicAuthUser, mg.APIKey()) - resp := SubaccountResponse{} + resp := mtypes.SubaccountResponse{} err := postResponseFromJSON(ctx, r, nil, &resp) return resp, err } // DisableSubaccount instructs Mailgun to disable subaccount. -func (mg *MailgunImpl) DisableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) { +func (mg *MailgunImpl) DisableSubaccount(ctx context.Context, subaccountId string) (mtypes.SubaccountResponse, error) { r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountId + "/" + "disable") r.setClient(mg.client) r.setBasicAuth(basicAuthUser, mg.APIKey()) - resp := SubaccountResponse{} + resp := mtypes.SubaccountResponse{} err := postResponseFromJSON(ctx, r, nil, &resp) return resp, err } diff --git a/subaccounts_test.go b/subaccounts_test.go index a2cb0772..3032b3e8 100644 --- a/subaccounts_test.go +++ b/subaccounts_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +27,7 @@ func TestListSubaccounts(t *testing.T) { ctx := context.Background() - var page []mailgun.Subaccount + var page []mtypes.Subaccount for iterator.Next(ctx, &page) { for _, d := range page { t.Logf("TestListSubaccounts: %#v\n", d) @@ -47,11 +48,11 @@ func TestGetSubaccount(t *testing.T) { iterator := mg.ListSubaccounts(nil) require.NotNil(t, iterator) - page := make([]mailgun.Subaccount, 0, 1) + page := make([]mtypes.Subaccount, 0, 1) require.True(t, iterator.Next(context.Background(), &page)) require.NoError(t, iterator.Err()) - resp, err := mg.GetSubaccount(ctx, page[0].Id) + resp, err := mg.GetSubaccount(ctx, page[0].ID) require.NoError(t, err) require.NotNil(t, resp) } diff --git a/tags.go b/tags.go index f21e922c..5e7cbef7 100644 --- a/tags.go +++ b/tags.go @@ -4,20 +4,9 @@ import ( "context" "net/url" "strconv" - "time" -) - -type Tag struct { - Value string `json:"tag"` - Description string `json:"description"` - FirstSeen *time.Time `json:"first-seen,omitempty"` - LastSeen *time.Time `json:"last-seen,omitempty"` -} -type tagsResponse struct { - Items []Tag `json:"items"` - Paging Paging `json:"paging"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) type ListTagOptions struct { // Restrict the page size to this limit @@ -36,11 +25,11 @@ func (mg *MailgunImpl) DeleteTag(ctx context.Context, domain, tag string) error } // GetTag retrieves metadata about the tag from the api -func (mg *MailgunImpl) GetTag(ctx context.Context, domain, tag string) (Tag, error) { +func (mg *MailgunImpl) GetTag(ctx context.Context, domain, tag string) (mtypes.Tag, error) { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, tagsEndpoint, domain) + "/" + tag) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var tagItem Tag + var tagItem mtypes.Tag err := getResponseFromJSON(ctx, r, &tagItem) return tagItem, err } @@ -70,20 +59,20 @@ func (mg *MailgunImpl) ListTags(domain string, opts *ListTagOptions) *TagIterato uri, err := req.generateUrlWithParameters() return &TagIterator{ - tagsResponse: tagsResponse{Paging: Paging{Next: uri, First: uri}}, + TagsResponse: mtypes.TagsResponse{Paging: mtypes.Paging{Next: uri, First: uri}}, err: err, mg: mg, } } type TagIterator struct { - tagsResponse + mtypes.TagsResponse mg Mailgun err error } // Next returns the next page in the list of tags -func (ti *TagIterator) Next(ctx context.Context, items *[]Tag) bool { +func (ti *TagIterator) Next(ctx context.Context, items *[]mtypes.Tag) bool { if ti.err != nil { return false } @@ -102,7 +91,7 @@ func (ti *TagIterator) Next(ctx context.Context, items *[]Tag) bool { } // Previous returns the previous page in the list of tags -func (ti *TagIterator) Previous(ctx context.Context, items *[]Tag) bool { +func (ti *TagIterator) Previous(ctx context.Context, items *[]mtypes.Tag) bool { if ti.err != nil { return false } @@ -125,7 +114,7 @@ func (ti *TagIterator) Previous(ctx context.Context, items *[]Tag) bool { } // First returns the first page in the list of tags -func (ti *TagIterator) First(ctx context.Context, items *[]Tag) bool { +func (ti *TagIterator) First(ctx context.Context, items *[]mtypes.Tag) bool { if ti.err != nil { return false } @@ -138,7 +127,7 @@ func (ti *TagIterator) First(ctx context.Context, items *[]Tag) bool { } // Last returns the last page in the list of tags -func (ti *TagIterator) Last(ctx context.Context, items *[]Tag) bool { +func (ti *TagIterator) Last(ctx context.Context, items *[]mtypes.Tag) bool { if ti.err != nil { return false } @@ -160,7 +149,7 @@ func (ti *TagIterator) fetch(ctx context.Context, uri string) error { req := newHTTPRequest(uri) req.setClient(ti.mg.HTTPClient()) req.setBasicAuth(basicAuthUser, ti.mg.APIKey()) - return getResponseFromJSON(ctx, req, &ti.tagsResponse) + return getResponseFromJSON(ctx, req, &ti.TagsResponse) } func canFetchPage(slug string) bool { diff --git a/tags_test.go b/tags_test.go index 9d890b0c..e984c60e 100644 --- a/tags_test.go +++ b/tags_test.go @@ -7,6 +7,7 @@ import ( "github.com/mailgun/errors" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -39,7 +40,7 @@ func TestTags(t *testing.T) { // Should return a list of available tags it := mg.ListTags(testDomain, nil) - var page []mailgun.Tag + var page []mtypes.Tag for it.Next(ctx, &page) { require.True(t, len(page) != 0) } @@ -48,7 +49,7 @@ func TestTags(t *testing.T) { // Should return a limited list of available tags cursor := mg.ListTags(testDomain, &mailgun.ListTagOptions{Limit: 1}) - var tags []mailgun.Tag + var tags []mtypes.Tag for cursor.Next(ctx, &tags) { require.Len(t, tags, 1) } diff --git a/template.go b/template.go index 8951beaf..09ae09b9 100644 --- a/template.go +++ b/template.go @@ -4,35 +4,12 @@ import ( "context" "errors" "strconv" -) - -type TemplateEngine string -// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine -const ( - TemplateEngineHandlebars = TemplateEngine("handlebars") - TemplateEngineGo = TemplateEngine("go") + "github.com/mailgun/mailgun-go/v4/mtypes" ) -type Template struct { - Name string `json:"name"` - Description string `json:"description"` - CreatedAt RFC2822Time `json:"createdAt"` - Version TemplateVersion `json:"version,omitempty"` -} - -type templateResp struct { - Item Template `json:"template"` - Message string `json:"message"` -} - -type templateListResp struct { - Items []Template `json:"items"` - Paging Paging `json:"paging"` -} - // Create a new template which can be used to attach template versions to -func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, template *Template) error { +func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, template *mtypes.Template) error { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -59,7 +36,7 @@ func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, templa payload.addValue("tag", template.Version.Tag) } - var resp templateResp + var resp mtypes.TemplateResp if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil { return err } @@ -68,22 +45,22 @@ func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, templa } // GetTemplate gets a template given the template name -func (mg *MailgunImpl) GetTemplate(ctx context.Context, domain, name string) (Template, error) { +func (mg *MailgunImpl) GetTemplate(ctx context.Context, domain, name string) (mtypes.Template, error) { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + name) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) r.addParameter("active", "yes") - var resp templateResp + var resp mtypes.TemplateResp err := getResponseFromJSON(ctx, r, &resp) if err != nil { - return Template{}, err + return mtypes.Template{}, err } return resp.Item, nil } // Update the name and description of a template -func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, template *Template) error { +func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, template *mtypes.Template) error { if template.Name == "" { return errors.New("UpdateTemplate() Template.Name cannot be empty") } @@ -100,7 +77,7 @@ func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, templa p.addValue("description", template.Description) } - var resp templateResp + var resp mtypes.TemplateResp err := putResponseFromJSON(ctx, r, p, &resp) if err != nil { return err @@ -119,7 +96,7 @@ func (mg *MailgunImpl) DeleteTemplate(ctx context.Context, domain, name string) } type TemplatesIterator struct { - templateListResp + mtypes.ListTemplateResp mg Mailgun err error } @@ -145,7 +122,7 @@ func (mg *MailgunImpl) ListTemplates(domain string, opts *ListTemplateOptions) * url, err := r.generateUrlWithParameters() return &TemplatesIterator{ mg: mg, - templateListResp: templateListResp{Paging: Paging{Next: url, First: url}}, + ListTemplateResp: mtypes.ListTemplateResp{Paging: mtypes.Paging{Next: url, First: url}}, err: err, } } @@ -158,7 +135,7 @@ func (ti *TemplatesIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool { +func (ti *TemplatesIterator) Next(ctx context.Context, items *[]mtypes.Template) bool { if ti.err != nil { return false } @@ -166,7 +143,7 @@ func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool { if ti.err != nil { return false } - cpy := make([]Template, len(ti.Items)) + cpy := make([]mtypes.Template, len(ti.Items)) copy(cpy, ti.Items) *items = cpy @@ -176,7 +153,7 @@ func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool { // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool { +func (ti *TemplatesIterator) First(ctx context.Context, items *[]mtypes.Template) bool { if ti.err != nil { return false } @@ -184,7 +161,7 @@ func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool if ti.err != nil { return false } - cpy := make([]Template, len(ti.Items)) + cpy := make([]mtypes.Template, len(ti.Items)) copy(cpy, ti.Items) *items = cpy return true @@ -194,7 +171,7 @@ func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool { +func (ti *TemplatesIterator) Last(ctx context.Context, items *[]mtypes.Template) bool { if ti.err != nil { return false } @@ -202,7 +179,7 @@ func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool { if ti.err != nil { return false } - cpy := make([]Template, len(ti.Items)) + cpy := make([]mtypes.Template, len(ti.Items)) copy(cpy, ti.Items) *items = cpy return true @@ -211,7 +188,7 @@ func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool { // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]Template) bool { +func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]mtypes.Template) bool { if ti.err != nil { return false } @@ -222,7 +199,7 @@ func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]Template) bo if ti.err != nil { return false } - cpy := make([]Template, len(ti.Items)) + cpy := make([]mtypes.Template, len(ti.Items)) copy(cpy, ti.Items) *items = cpy @@ -235,5 +212,5 @@ func (ti *TemplatesIterator) fetch(ctx context.Context, url string) error { r.setClient(ti.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, ti.mg.APIKey()) - return getResponseFromJSON(ctx, r, &ti.templateListResp) + return getResponseFromJSON(ctx, r, &ti.ListTemplateResp) } diff --git a/template_test.go b/template_test.go index 0fbf3deb..f1c3e9a1 100644 --- a/template_test.go +++ b/template_test.go @@ -8,6 +8,7 @@ import ( "github.com/mailgun/errors" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,7 +23,7 @@ func TestTemplateCRUD(t *testing.T) { findTemplate := func(name string) bool { it := mg.ListTemplates(testDomain, nil) - var page []mailgun.Template + var page []mtypes.Template for it.Next(ctx, &page) { for _, template := range page { if template.Name == name { @@ -40,7 +41,7 @@ func TestTemplateCRUD(t *testing.T) { UpdatedDesc = "Mailgun-Go Test Updated Description" ) - tmpl := mailgun.Template{ + tmpl := mtypes.Template{ Name: Name, Description: Description, } diff --git a/template_versions.go b/template_versions.go index 65b9a0ba..31e472ce 100644 --- a/template_versions.go +++ b/template_versions.go @@ -3,27 +3,12 @@ package mailgun import ( "context" "strconv" -) - -type TemplateVersion struct { - Tag string `json:"tag"` - Template string `json:"template,omitempty"` - Engine TemplateEngine `json:"engine"` - CreatedAt RFC2822Time `json:"createdAt"` - Comment string `json:"comment"` - Active bool `json:"active"` -} -type templateVersionListResp struct { - Template struct { - Template - Versions []TemplateVersion `json:"versions,omitempty"` - } `json:"template"` - Paging Paging `json:"paging"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // AddTemplateVersion adds a template version to a template -func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error { +func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions") r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -44,7 +29,7 @@ func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateN payload.addValue("active", boolToString(version.Active)) } - var resp templateResp + var resp mtypes.TemplateResp if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil { return err } @@ -53,21 +38,21 @@ func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateN } // GetTemplateVersion gets a specific version of a template -func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (TemplateVersion, error) { +func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (mtypes.TemplateVersion, error) { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions/" + tag) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var resp templateResp + var resp mtypes.TemplateResp err := getResponseFromJSON(ctx, r, &resp) if err != nil { - return TemplateVersion{}, err + return mtypes.TemplateVersion{}, err } return resp.Item.Version, nil } // Update the comment and mark a version of a template active -func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error { +func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions/" + version.Tag) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) @@ -83,7 +68,7 @@ func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templa p.addValue("template", version.Template) } - var resp templateResp + var resp mtypes.TemplateResp err := putResponseFromJSON(ctx, r, p, &resp) if err != nil { return err @@ -102,7 +87,7 @@ func (mg *MailgunImpl) DeleteTemplateVersion(ctx context.Context, domain, templa } type TemplateVersionsIterator struct { - templateVersionListResp + mtypes.TemplateVersionListResp mg Mailgun err error } @@ -120,7 +105,7 @@ func (mg *MailgunImpl) ListTemplateVersions(domain, templateName string, opts *L url, err := r.generateUrlWithParameters() return &TemplateVersionsIterator{ mg: mg, - templateVersionListResp: templateVersionListResp{Paging: Paging{Next: url, First: url}}, + TemplateVersionListResp: mtypes.TemplateVersionListResp{Paging: mtypes.Paging{Next: url, First: url}}, err: err, } } @@ -133,7 +118,7 @@ func (li *TemplateVersionsIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateVersion) bool { +func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]mtypes.TemplateVersion) bool { if li.err != nil { return false } @@ -141,7 +126,7 @@ func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateV if li.err != nil { return false } - cpy := make([]TemplateVersion, len(li.Template.Versions)) + cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions)) copy(cpy, li.Template.Versions) *items = cpy @@ -151,7 +136,7 @@ func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateV // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]TemplateVersion) bool { +func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]mtypes.TemplateVersion) bool { if li.err != nil { return false } @@ -159,7 +144,7 @@ func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]Template if li.err != nil { return false } - cpy := make([]TemplateVersion, len(li.Template.Versions)) + cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions)) copy(cpy, li.Template.Versions) *items = cpy return true @@ -169,7 +154,7 @@ func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]Template // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateVersion) bool { +func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]mtypes.TemplateVersion) bool { if li.err != nil { return false } @@ -177,7 +162,7 @@ func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateV if li.err != nil { return false } - cpy := make([]TemplateVersion, len(li.Template.Versions)) + cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions)) copy(cpy, li.Template.Versions) *items = cpy return true @@ -186,7 +171,7 @@ func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateV // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]TemplateVersion) bool { +func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]mtypes.TemplateVersion) bool { if li.err != nil { return false } @@ -197,7 +182,7 @@ func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]Templ if li.err != nil { return false } - cpy := make([]TemplateVersion, len(li.Template.Versions)) + cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions)) copy(cpy, li.Template.Versions) *items = cpy @@ -210,5 +195,5 @@ func (li *TemplateVersionsIterator) fetch(ctx context.Context, url string) error r.setClient(li.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, li.mg.APIKey()) - return getResponseFromJSON(ctx, r, &li.templateVersionListResp) + return getResponseFromJSON(ctx, r, &li.TemplateVersionListResp) } diff --git a/template_versions_test.go b/template_versions_test.go index 39c1adfd..ff194e60 100644 --- a/template_versions_test.go +++ b/template_versions_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -19,7 +20,7 @@ func TestTemplateVersionsCRUD(t *testing.T) { findVersion := func(templateName, tag string) bool { it := mg.ListTemplateVersions(testDomain, templateName, nil) - var page []mailgun.TemplateVersion + var page []mtypes.TemplateVersion for it.Next(ctx, &page) { for _, v := range page { if v.Tag == tag { @@ -38,26 +39,26 @@ func TestTemplateVersionsCRUD(t *testing.T) { Tag = "v1" ) - tmpl := mailgun.Template{ + tmpl := mtypes.Template{ Name: randomString(10, "Mailgun-go-TestTemplateVersionsCRUD-"), } // Create a template require.NoError(t, mg.CreateTemplate(ctx, testDomain, &tmpl)) - version := mailgun.TemplateVersion{ + version := mtypes.TemplateVersion{ Tag: Tag, Comment: Comment, Template: Template, Active: true, - Engine: mailgun.TemplateEngineGo, + Engine: mtypes.TemplateEngineGo, } // Add a version version require.NoError(t, mg.AddTemplateVersion(ctx, testDomain, tmpl.Name, &version)) assert.Equal(t, Tag, version.Tag) assert.Equal(t, Comment, version.Comment) - assert.Equal(t, mailgun.TemplateEngineGo, version.Engine) + assert.Equal(t, mtypes.TemplateEngineGo, version.Engine) // Ensure the version is in the list require.True(t, findVersion(tmpl.Name, version.Tag)) @@ -75,12 +76,12 @@ func TestTemplateVersionsCRUD(t *testing.T) { assert.Equal(t, Template+"updated", updated.Template) // Add a new active Version - version2 := mailgun.TemplateVersion{ + version2 := mtypes.TemplateVersion{ Tag: "v2", Comment: Comment, Template: Template, Active: true, - Engine: mailgun.TemplateEngineGo, + Engine: mtypes.TemplateEngineGo, } require.NoError(t, mg.AddTemplateVersion(ctx, testDomain, tmpl.Name, &version2)) diff --git a/unsubscribes.go b/unsubscribes.go index d76b419c..71488592 100644 --- a/unsubscribes.go +++ b/unsubscribes.go @@ -3,19 +3,9 @@ package mailgun import ( "context" "strconv" -) - -type Unsubscribe struct { - CreatedAt RFC2822Time `json:"created_at,omitempty"` - Tags []string `json:"tags,omitempty"` - ID string `json:"id,omitempty"` - Address string `json:"address"` -} -type unsubscribesResponse struct { - Paging Paging `json:"paging"` - Items []Unsubscribe `json:"items"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // Fetches the list of unsubscribes func (mg *MailgunImpl) ListUnsubscribes(domain string, opts *ListOptions) *UnsubscribesIterator { @@ -29,14 +19,14 @@ func (mg *MailgunImpl) ListUnsubscribes(domain string, opts *ListOptions) *Unsub } url, err := r.generateUrlWithParameters() return &UnsubscribesIterator{ - mg: mg, - unsubscribesResponse: unsubscribesResponse{Paging: Paging{Next: url, First: url}}, - err: err, + mg: mg, + ListUnsubscribesResponse: mtypes.ListUnsubscribesResponse{Paging: mtypes.Paging{Next: url, First: url}}, + err: err, } } type UnsubscribesIterator struct { - unsubscribesResponse + mtypes.ListUnsubscribesResponse mg Mailgun err error } @@ -49,7 +39,7 @@ func (ci *UnsubscribesIterator) Err() error { // Next retrieves the next page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error -func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe) bool { +func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]mtypes.Unsubscribe) bool { if ci.err != nil { return false } @@ -57,7 +47,7 @@ func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe) if ci.err != nil { return false } - cpy := make([]Unsubscribe, len(ci.Items)) + cpy := make([]mtypes.Unsubscribe, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -67,7 +57,7 @@ func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe) // First retrieves the first page of items from the api. Returns false if there // was an error. It also sets the iterator object to the first page. // Use `.Err()` to retrieve the error. -func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe) bool { +func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]mtypes.Unsubscribe) bool { if ci.err != nil { return false } @@ -75,7 +65,7 @@ func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe) if ci.err != nil { return false } - cpy := make([]Unsubscribe, len(ci.Items)) + cpy := make([]mtypes.Unsubscribe, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -85,7 +75,7 @@ func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe) // Calling Last() is invalid unless you first call First() or Next() // Returns false if there was an error. It also sets the iterator object // to the last page. Use `.Err()` to retrieve the error. -func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe) bool { +func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]mtypes.Unsubscribe) bool { if ci.err != nil { return false } @@ -93,7 +83,7 @@ func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe) if ci.err != nil { return false } - cpy := make([]Unsubscribe, len(ci.Items)) + cpy := make([]mtypes.Unsubscribe, len(ci.Items)) copy(cpy, ci.Items) *items = cpy return true @@ -102,7 +92,7 @@ func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe) // Previous retrieves the previous page of items from the api. Returns false when there // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve // the error if any -func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]Unsubscribe) bool { +func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]mtypes.Unsubscribe) bool { if ci.err != nil { return false } @@ -113,7 +103,7 @@ func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]Unsubscri if ci.err != nil { return false } - cpy := make([]Unsubscribe, len(ci.Items)) + cpy := make([]mtypes.Unsubscribe, len(ci.Items)) copy(cpy, ci.Items) *items = cpy @@ -126,16 +116,16 @@ func (ci *UnsubscribesIterator) fetch(ctx context.Context, url string) error { r.setClient(ci.mg.HTTPClient()) r.setBasicAuth(basicAuthUser, ci.mg.APIKey()) - return getResponseFromJSON(ctx, r, &ci.unsubscribesResponse) + return getResponseFromJSON(ctx, r, &ci.ListUnsubscribesResponse) } // Retreives a single unsubscribe record. Can be used to check if a given address is present in the list of unsubscribed users. -func (mg *MailgunImpl) GetUnsubscribe(ctx context.Context, domain, address string) (Unsubscribe, error) { +func (mg *MailgunImpl) GetUnsubscribe(ctx context.Context, domain, address string) (mtypes.Unsubscribe, error) { r := newHTTPRequest(generateApiV3UrlWithTarget(mg, unsubscribesEndpoint, domain, address)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - envelope := Unsubscribe{} + envelope := mtypes.Unsubscribe{} err := getResponseFromJSON(ctx, r, &envelope) return envelope, err @@ -154,7 +144,7 @@ func (mg *MailgunImpl) CreateUnsubscribe(ctx context.Context, domain, address, t } // CreateUnsubscribes adds multiple e-mail addresses to the domain's unsubscription table. -func (mg *MailgunImpl) CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []Unsubscribe) error { +func (mg *MailgunImpl) CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []mtypes.Unsubscribe) error { r := newHTTPRequest(generateApiV3UrlWithDomain(mg, unsubscribesEndpoint, domain)) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) diff --git a/unsubscribes_test.go b/unsubscribes_test.go index deb04f64..74a6ad19 100644 --- a/unsubscribes_test.go +++ b/unsubscribes_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/require" ) @@ -22,7 +23,7 @@ func TestCreateUnsubscriber(t *testing.T) { } func TestCreateUnsubscribes(t *testing.T) { - unsubscribes := []mailgun.Unsubscribe{ + unsubscribes := []mtypes.Unsubscribe{ { Address: randomEmail("unsubcribe", os.Getenv("MG_DOMAIN")), }, @@ -49,7 +50,7 @@ func TestListUnsubscribes(t *testing.T) { ctx := context.Background() it := mg.ListUnsubscribes(testDomain, nil) - var page []mailgun.Unsubscribe + var page []mtypes.Unsubscribe for it.Next(ctx, &page) { t.Logf("Received %d unsubscribe records.\n", len(page)) if len(page) > 0 { diff --git a/validation.go b/validation.go index 1fedc36e..3e7300e4 100644 --- a/validation.go +++ b/validation.go @@ -3,54 +3,14 @@ package mailgun import ( "context" "fmt" -) - -// ValidateEmailResponse records basic facts about a validated e-mail address. -// See the ValidateEmail method and example for more details. -type ValidateEmailResponse struct { - // Echoes the address provided. - Address string `json:"address"` - - // Indicates whether Mailgun thinks the address is from a known - // disposable mailbox provider. - IsDisposableAddress bool `json:"is_disposable_address"` - - // Indicates whether Mailgun thinks the address is an email distribution list. - IsRoleAddress bool `json:"is_role_address"` - - // A list of potential reasons why a specific validation may be unsuccessful. (Available in the v4 response) - Reason []string `json:"reason"` - - // Result - Result string `json:"result"` - - // Risk assessment for the provided email. - Risk string `json:"risk"` - LastSeen int64 `json:"last_seen,omitempty"` - - // Provides a simple recommendation in case the address is invalid or - // Mailgun thinks you might have a typo. May be empty, in which case - // Mailgun has no recommendation to give. - DidYouMean string `json:"did_you_mean,omitempty"` - - // Engagement results are a macro-level view that explain an email recipient’s propensity to engage. - // https://documentation.mailgun.com/docs/inboxready/mailgun-validate/validate_engagement/ - Engagement *EngagementData `json:"engagement,omitempty"` - - RootAddress string `json:"root_address,omitempty"` -} - -type EngagementData struct { - Engaging bool `json:"engaging"` - IsBot bool `json:"is_bot"` - Behavior string `json:"behavior,omitempty"` -} + "github.com/mailgun/mailgun-go/v4/mtypes" +) // ValidateEmail performs various checks on the email address provided to ensure it's correctly formatted. // It may also be used to break an email address into its sub-components. // https://documentation.mailgun.com/docs/inboxready/mailgun-validate/single-valid-ir/ -func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (ValidateEmailResponse, error) { +func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (mtypes.ValidateEmailResponse, error) { r := newHTTPRequest(fmt.Sprintf("%s/v4/address/validate", mg.APIBase())) r.setClient(mg.HTTPClient()) r.addParameter("address", email) @@ -59,10 +19,10 @@ func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxV } r.setBasicAuth(basicAuthUser, mg.APIKey()) - var res ValidateEmailResponse + var res mtypes.ValidateEmailResponse err := getResponseFromJSON(ctx, r, &res) if err != nil { - return ValidateEmailResponse{}, err + return mtypes.ValidateEmailResponse{}, err } return res, nil diff --git a/webhooks.go b/webhooks.go index 04212b3e..68e58f1e 100644 --- a/webhooks.go +++ b/webhooks.go @@ -11,22 +11,9 @@ import ( "fmt" "io" - "github.com/mailgun/mailgun-go/v4/events" + "github.com/mailgun/mailgun-go/v4/mtypes" ) -type UrlOrUrls struct { - Urls []string `json:"urls"` - Url string `json:"url"` -} - -type WebHooksListResponse struct { - Webhooks map[string]UrlOrUrls `json:"webhooks"` -} - -type WebHookResponse struct { - Webhook UrlOrUrls `json:"webhook"` -} - // ListWebhooks returns the complete set of webhooks configured for your domain. // Note that a zero-length mapping is not an error. func (mg *MailgunImpl) ListWebhooks(ctx context.Context, domain string) (map[string][]string, error) { @@ -34,7 +21,7 @@ func (mg *MailgunImpl) ListWebhooks(ctx context.Context, domain string) (map[str r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var body WebHooksListResponse + var body mtypes.WebHooksListResponse err := getResponseFromJSON(ctx, r, &body) if err != nil { return nil, err @@ -80,7 +67,7 @@ func (mg *MailgunImpl) GetWebhook(ctx context.Context, domain, name string) ([]s r := newHTTPRequest(generateV3DomainsApiUrl(mg, webhooksEndpoint, domain) + "/" + name) r.setClient(mg.HTTPClient()) r.setBasicAuth(basicAuthUser, mg.APIKey()) - var body WebHookResponse + var body mtypes.WebHookResponse if err := getResponseFromJSON(ctx, r, &body); err != nil { return nil, err } @@ -107,21 +94,8 @@ func (mg *MailgunImpl) UpdateWebhook(ctx context.Context, domain, name string, u return err } -// Signature represents the signature portion of the webhook POST body -type Signature struct { - TimeStamp string `json:"timestamp"` - Token string `json:"token"` - Signature string `json:"signature"` -} - -// WebhookPayload represents the JSON payload provided when a Webhook is called by mailgun -type WebhookPayload struct { - Signature Signature `json:"signature"` - EventData events.RawJSON `json:"event-data"` -} - // VerifyWebhookSignature - use this method to parse the webhook signature given as JSON in the webhook response -func (mg *MailgunImpl) VerifyWebhookSignature(sig Signature) (verified bool, err error) { +func (mg *MailgunImpl) VerifyWebhookSignature(sig mtypes.Signature) (verified bool, err error) { webhookSigningKey := mg.WebhookSigningKey() if webhookSigningKey == "" { return false, fmt.Errorf("webhook signing key is not set") diff --git a/webhooks_test.go b/webhooks_test.go index 9f73aa6c..f2bea9d9 100644 --- a/webhooks_test.go +++ b/webhooks_test.go @@ -14,6 +14,7 @@ import ( "testing" "github.com/mailgun/mailgun-go/v4" + "github.com/mailgun/mailgun-go/v4/mtypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -86,7 +87,7 @@ func TestVerifyWebhookSignature(t *testing.T) { for _, v := range signedTests { fields := getSignatureFields(mg.WebhookSigningKey(), v) - sig := mailgun.Signature{ + sig := mtypes.Signature{ TimeStamp: fields["timestamp"], Token: fields["token"], Signature: fields["signature"],