Skip to content

Commit

Permalink
DE-1384 Domain agnostic API (#376)
Browse files Browse the repository at this point in the history
  • Loading branch information
vtopc authored Dec 25, 2024
1 parent 7933fc9 commit 49c465a
Show file tree
Hide file tree
Showing 52 changed files with 681 additions and 683 deletions.
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var privateAPIKey = "your-private-key"

func main() {
// Create an instance of the Mailgun Client
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
mg := mailgun.NewMailgun(privateAPIKey)

//When you have an EU-domain, you must specify the endpoint:
//mg.SetAPIBase("https://api.eu.mailgun.net/v3")
Expand Down Expand Up @@ -79,9 +79,9 @@ import (
func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
mg := mailgun.NewMailgun("your-private-key")

it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
it := mg.ListEvents("your-domain.com", &mailgun.ListEventOptions{Limit: 100})

var page []mailgun.Event

Expand Down Expand Up @@ -135,12 +135,12 @@ import (
func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
mg := mailgun.NewMailgun("your-private-key")

begin := time.Now().Add(time.Second * -3)

// Very short poll interval
it := mg.PollEvents(&mailgun.ListEventOptions{
it := mg.PollEvents("your-domain.com", &mailgun.ListEventOptions{
// Only events with a timestamp after this date/time will be returned
Begin: begin,
// How often we poll the api for new events
Expand Down Expand Up @@ -210,11 +210,10 @@ import (
func main() {
// You can find the Private API Key in your Account Menu, under "Settings":
// (https://app.mailgun.com/settings/api_security)
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg := mailgun.NewMailgun("private-api-key")
mg.SetWebhookSigningKey("webhook-signing-key")

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

var payload mailgun.WebhookPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
fmt.Printf("decode JSON error: %s", err)
Expand Down Expand Up @@ -284,7 +283,7 @@ var privateAPIKey = "your-private-key"

func main() {
// Create an instance of the Mailgun Client
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
mg := mailgun.NewMailgun(privateAPIKey)

sender := "[email protected]"
subject := "HTML email!"
Expand All @@ -306,7 +305,7 @@ func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()

// Send the message with a 10 second timeout
// Send the message with a 10-second timeout
resp, id, err := mg.Send(ctx, message)

if err != nil {
Expand Down Expand Up @@ -343,7 +342,7 @@ var privateAPIKey = "your-private-key"

func main() {
// Create an instance of the Mailgun Client
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
mg := mailgun.NewMailgun(privateAPIKey)

sender := "[email protected]"
subject := "Fancy subject!"
Expand Down Expand Up @@ -379,7 +378,7 @@ and click on the "Go" button at the top of the page.
European customers will need to change the default API Base to access your domains

```go
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg := mailgun.NewMailgun("private-api-key")
mg.SetAPIBase(mailgun.APIBaseEU)
```

Expand All @@ -390,7 +389,6 @@ mg.SetAPIBase(mailgun.APIBaseEU)
To run the tests various environment variables must be set. These are:

* `MG_DOMAIN` is the domain name - this is a value registered in the Mailgun admin interface.
* `MG_PUBLIC_API_KEY` is the Public Validation API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/settings/api_security)
* `MG_API_KEY` is the Private API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/settings/api_security)
* `MG_EMAIL_TO` is the email address used in various sending tests.

Expand Down
2 changes: 1 addition & 1 deletion acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// Return the variable missing which caused the test to be skipped
func SkipNetworkTest() string {
for _, env := range []string{"MG_DOMAIN", "MG_API_KEY", "MG_EMAIL_TO", "MG_PUBLIC_API_KEY"} {
for _, env := range []string{"MG_DOMAIN", "MG_API_KEY", "MG_EMAIL_TO"} {
if os.Getenv(env) == "" {
return fmt.Sprintf("'%s' missing from environment skipping...", env)
}
Expand Down
23 changes: 10 additions & 13 deletions analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ type MetricsPagination struct {

// ListMetrics returns domain/account metrics.
//
// To filter by domain:
//
// opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{
// Attribute: "domain",
// Comparator: "=",
// LabeledValues: []mailgun.MetricsLabeledValue{{Label: "example.com", Value: "example.com"}},
// }}
//
// NOTE: Only for v1 API. To use the /v1 version define MG_URL in the environment variable
// as `https://api.mailgun.net/v1` or set `mg.SetAPIBase("https://api.mailgun.net/v1")`
//
Expand All @@ -31,23 +39,12 @@ func (mg *MailgunImpl) ListMetrics(opts MetricsOptions) (*MetricsIterator, error
return nil, errors.New("only v1 API is supported")
}

domain := mg.Domain()
if domain != "" {
domainFilter := MetricsFilterPredicate{
Attribute: "domain",
Comparator: "=",
LabeledValues: []MetricsLabeledValue{{Label: domain, Value: domain}},
}

opts.Filter.BoolGroupAnd = append(opts.Filter.BoolGroupAnd, domainFilter)
}

if opts.Pagination.Limit == 0 {
opts.Pagination.Limit = 10
}

req := newHTTPRequest(generatePublicApiUrl(mg, metricsEndpoint))
req.setClient(mg.Client())
req := newHTTPRequest(generateApiUrl(mg, metricsEndpoint))
req.setClient(mg.HTTPClient())
req.setBasicAuth(basicAuthUser, mg.APIKey())

return &MetricsIterator{
Expand Down
8 changes: 7 additions & 1 deletion analytics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func TestListMetrics(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL1())

start, _ := mailgun.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000")
Expand All @@ -23,6 +23,12 @@ func TestListMetrics(t *testing.T) {
Limit: 10,
},
}
// filter by domain
opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{
Attribute: "domain",
Comparator: "=",
LabeledValues: []mailgun.MetricsLabeledValue{{Label: testDomain, Value: testDomain}},
}}

wantResp := mailgun.MetricsResponse{
Start: start,
Expand Down
6 changes: 3 additions & 3 deletions attachments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func createAttachment(t *testing.T) string {
}

func TestMultipleAttachments(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetAPIBase(server.URL())
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL3())

var ctx = context.Background()

Expand Down Expand Up @@ -56,7 +56,7 @@ func TestMultipleAttachments(t *testing.T) {
}

func findAcceptedMessage(mg mailgun.Mailgun, id string) (*events.Accepted, error) {
it := mg.ListEvents(nil)
it := mg.ListEvents(testDomain, nil)

var page []mailgun.Event
for it.Next(context.Background(), &page) {
Expand Down
38 changes: 19 additions & 19 deletions bounces.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ type bouncesListResponse struct {
// The results include the total number of bounces (regardless of skip or limit settings),
// and the slice of bounces specified, if successful.
// Note that the length of the slice may be smaller than the total number of bounces.
func (mg *MailgunImpl) ListBounces(opts *ListOptions) *BouncesIterator {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
r.setClient(mg.Client())
func (mg *MailgunImpl) ListBounces(domain string, opts *ListOptions) *BouncesIterator {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain))
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())
if opts != nil {
if opts.Limit != 0 {
Expand Down Expand Up @@ -139,16 +139,16 @@ func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool {
func (ci *BouncesIterator) fetch(ctx context.Context, url string) error {
ci.Items = nil
r := newHTTPRequest(url)
r.setClient(ci.mg.Client())
r.setClient(ci.mg.HTTPClient())
r.setBasicAuth(basicAuthUser, ci.mg.APIKey())

return getResponseFromJSON(ctx, r, &ci.bouncesListResponse)
}

// GetBounce retrieves a single bounce record, if any exist, for the given recipient address.
func (mg *MailgunImpl) GetBounce(ctx context.Context, address string) (Bounce, error) {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint) + "/" + address)
r.setClient(mg.Client())
func (mg *MailgunImpl) GetBounce(ctx context.Context, domain, address string) (Bounce, error) {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain) + "/" + address)
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())

var response Bounce
Expand All @@ -172,9 +172,9 @@ func (mg *MailgunImpl) GetBounce(ctx context.Context, address string) (Bounce, e
//
// Note that both code and error exist as strings, even though
// code will report as a number.
func (mg *MailgunImpl) AddBounce(ctx context.Context, address, code, bounceError string) error {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
r.setClient(mg.Client())
func (mg *MailgunImpl) AddBounce(ctx context.Context, domain, address, code, bounceError string) error {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain))
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())

payload := newUrlEncodedPayload()
Expand All @@ -190,9 +190,9 @@ func (mg *MailgunImpl) AddBounce(ctx context.Context, address, code, bounceError
}

// Add Bounces adds a list of bounces to the bounce list
func (mg *MailgunImpl) AddBounces(ctx context.Context, bounces []Bounce) error {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
r.setClient(mg.Client())
func (mg *MailgunImpl) AddBounces(ctx context.Context, domain string, bounces []Bounce) error {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain))
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())

payload := newJSONEncodedPayload(bounces)
Expand All @@ -202,18 +202,18 @@ func (mg *MailgunImpl) AddBounces(ctx context.Context, bounces []Bounce) error {
}

// DeleteBounce removes all bounces associted with the provided e-mail address.
func (mg *MailgunImpl) DeleteBounce(ctx context.Context, address string) error {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint) + "/" + address)
r.setClient(mg.Client())
func (mg *MailgunImpl) DeleteBounce(ctx context.Context, domain, address string) error {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain) + "/" + address)
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())
_, err := makeDeleteRequest(ctx, r)
return err
}

// DeleteBounceList removes all bounces in the bounce list
func (mg *MailgunImpl) DeleteBounceList(ctx context.Context) error {
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
r.setClient(mg.Client())
func (mg *MailgunImpl) DeleteBounceList(ctx context.Context, domain string) error {
r := newHTTPRequest(generateApiUrlWithDomain(mg, bouncesEndpoint, domain))
r.setClient(mg.HTTPClient())
r.setBasicAuth(basicAuthUser, mg.APIKey())
_, err := makeDeleteRequest(ctx, r)
return err
Expand Down
40 changes: 20 additions & 20 deletions bounces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
)

func TestGetBounces(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetAPIBase(server.URL())
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL3())

ctx := context.Background()
it := mg.ListBounces(nil)
it := mg.ListBounces(testDomain, nil)

var page []mailgun.Bounce
for it.Next(ctx, &page) {
Expand All @@ -30,13 +30,13 @@ func TestGetBounces(t *testing.T) {
}

func TestGetSingleBounce(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetAPIBase(server.URL())
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL3())

ctx := context.Background()
exampleEmail := fmt.Sprintf("%s@%s", strings.ToLower(randomString(64, "")),
os.Getenv("MG_DOMAIN"))
_, err := mg.GetBounce(ctx, exampleEmail)
_, err := mg.GetBounce(ctx, testDomain, exampleEmail)
require.NotNil(t, err)

var ure *mailgun.UnexpectedResponseError
Expand All @@ -45,12 +45,12 @@ func TestGetSingleBounce(t *testing.T) {
}

func TestAddDelBounces(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetAPIBase(server.URL())
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL3())
ctx := context.Background()

findBounce := func(address string) bool {
it := mg.ListBounces(nil)
it := mg.ListBounces(testDomain, nil)
var page []mailgun.Bounce
for it.Next(ctx, &page) {
require.True(t, len(page) != 0)
Expand All @@ -71,7 +71,7 @@ func TestAddDelBounces(t *testing.T) {
exampleEmail := fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain)

// Add the bounce for our address.
err := mg.AddBounce(ctx, exampleEmail, "550", "TestAddDelBounces-generated error")
err := mg.AddBounce(ctx, testDomain, exampleEmail, "550", "TestAddDelBounces-generated error")
require.NoError(t, err)

// Give API some time to refresh cache
Expand All @@ -82,34 +82,34 @@ func TestAddDelBounces(t *testing.T) {
t.Fatalf("Expected bounce for address %s in list of bounces", exampleEmail)
}

bounce, err := mg.GetBounce(ctx, exampleEmail)
bounce, err := mg.GetBounce(ctx, testDomain, exampleEmail)
require.NoError(t, err)
if bounce.Address != exampleEmail {
t.Fatalf("Expected at least one bounce for %s", exampleEmail)
}
t.Logf("Bounce Created At: %s", bounce.CreatedAt)

// Delete it. This should put us back the way we were.
err = mg.DeleteBounce(ctx, exampleEmail)
err = mg.DeleteBounce(ctx, testDomain, exampleEmail)
require.NoError(t, err)

// Make sure we're back to the way we were.
if findBounce(exampleEmail) {
t.Fatalf("Un-expected bounce for address %s in list of bounces", exampleEmail)
}

_, err = mg.GetBounce(ctx, exampleEmail)
_, err = mg.GetBounce(ctx, testDomain, exampleEmail)
require.NotNil(t, err)
}

func TestAddDelBounceList(t *testing.T) {
mg := mailgun.NewMailgun(testDomain, testKey)
mg.SetAPIBase(server.URL())
mg := mailgun.NewMailgun(testKey)
mg.SetAPIBase(server.URL3())

ctx := context.Background()

findBounce := func(address string) bool {
it := mg.ListBounces(nil)
it := mg.ListBounces(testDomain, nil)
var page []mailgun.Bounce
for it.Next(ctx, &page) {
require.True(t, len(page) != 0)
Expand Down Expand Up @@ -147,15 +147,15 @@ func TestAddDelBounceList(t *testing.T) {
}

// Add the bounce for our address.
err = mg.AddBounces(ctx, bounces)
err = mg.AddBounces(ctx, testDomain, bounces)
require.NoError(t, err)

for _, expect := range bounces {
if !findBounce(expect.Address) {
t.Fatalf("Expected bounce for address %s in list of bounces", expect.Address)
}

bounce, err := mg.GetBounce(ctx, expect.Address)
bounce, err := mg.GetBounce(ctx, testDomain, expect.Address)
require.NoError(t, err)
if bounce.Address != expect.Address {
t.Fatalf("Expected at least one bounce for %s", expect.Address)
Expand All @@ -167,10 +167,10 @@ func TestAddDelBounceList(t *testing.T) {
}

// Delete the bounce list. This should put us back the way we were.
err = mg.DeleteBounceList(ctx)
err = mg.DeleteBounceList(ctx, testDomain)
require.NoError(t, err)

it := mg.ListBounces(nil)
it := mg.ListBounces(testDomain, nil)
var page []mailgun.Bounce
if it.Next(ctx, &page) {
t.Fatalf("Expected no item in the bounce list")
Expand Down
Loading

0 comments on commit 49c465a

Please sign in to comment.