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-1384 Domain agnostic API #376

Merged
merged 18 commits into from
Dec 25, 2024
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
Loading