Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

DE-1393 Move API Mailgun requests, responses, and helper types into a separate package #393

Merged
merged 10 commits into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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
}
Expand All @@ -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")
}
Expand All @@ -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 {
Expand Down
25 changes: 13 additions & 12 deletions analytics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -14,45 +15,45 @@ 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)),
FailedCount: ptr(uint64(1)),
},
},
},
Pagination: mailgun.MetricsPagination{
Pagination: mtypes.MetricsPagination{
Sort: "",
Skip: 0,
Limit: 10,
Expand All @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion attachments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
52 changes: 17 additions & 35 deletions bounces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
}
}
Expand All @@ -57,23 +39,23 @@ 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
}

// 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
}
ci.err = ci.fetch(ctx, ci.Paging.Next)
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

Expand All @@ -83,15 +65,15 @@ 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
}
ci.err = ci.fetch(ctx, ci.Paging.First)
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
Expand All @@ -101,15 +83,15 @@ 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
}
ci.err = ci.fetch(ctx, ci.Paging.Last)
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
Expand All @@ -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
}
Expand All @@ -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

Expand All @@ -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
}
Expand Down Expand Up @@ -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())
Expand Down
13 changes: 7 additions & 6 deletions bounces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/mailgun/mailgun-go/v4"
"github.com/mailgun/mailgun-go/v4/mtypes"
"github.com/stretchr/testify/require"
)

Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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),
Expand Down Expand Up @@ -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")
}
Expand Down
Loading