Repository: mailgun/mailgun-go
Pull request #290 (2022-09-07 17:19:34)

Add support for batch endpoints for adding bounces and complaints
Adds `AddComplaints` and `AddBounces` methods to support the batch mode for the associated endpoints

https://documentation.mailgun.com/en/latest/api-suppressions.html#add-multiple-complaints
https://documentation.mailgun.com/en/latest/api-suppressions.html#add-multiple-bounces


Patch:
diff --git a/bounces.go b/bounces.go
index 89ef094..4ecadf4 100644
--- a/bounces.go
+++ b/bounces.go
@@ -193,6 +193,18 @@ func (mg *MailgunImpl) AddBounce(ctx context.Context, address, code, error strin
 	return err
 }
 
+// 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())
+	r.setBasicAuth(basicAuthUser, mg.APIKey())
+
+	payload := newJSONEncodedPayload(bounces)
+
+	_, err := makePostRequest(ctx, r, payload)
+	return err
+}
+
 // 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)
diff --git a/spam_complaints.go b/spam_complaints.go
index 3f3f2ff..3304f8f 100644
--- a/spam_complaints.go
+++ b/spam_complaints.go
@@ -164,6 +164,22 @@ func (mg *MailgunImpl) CreateComplaint(ctx context.Context, address string) erro
 	return err
 }
 
+func (mg *MailgunImpl) CreateComplaints(ctx context.Context, addresses []string) error {
+	r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint))
+	r.setClient(mg.Client())
+	r.setBasicAuth(basicAuthUser, mg.APIKey())
+
+	body := make([]map[string]string, len(addresses))
+	for i, addr := range addresses {
+		body[i] = map[string]string{"address": addr}
+	}
+
+	payload := newJSONEncodedPayload(body)
+
+	_, err := makePostRequest(ctx, r, payload)
+	return err
+}
+
 // DeleteComplaint removes a previously registered e-mail address from the list of people who complained
 // of receiving spam from your domain.
 func (mg *MailgunImpl) DeleteComplaint(ctx context.Context, address string) error {


Test patch:
diff --git a/bounces_test.go b/bounces_test.go
index d9079b4..0c43a8e 100644
--- a/bounces_test.go
+++ b/bounces_test.go
@@ -103,7 +103,7 @@ func TestAddDelBounces(t *testing.T) {
 	ensure.NotNil(t, err)
 }
 
-func TestDelBounceList(t *testing.T) {
+func TestAddDelBounceList(t *testing.T) {
 	mg := mailgun.NewMailgun(testDomain, testKey)
 	mg.SetAPIBase(server.URL())
 
@@ -127,25 +127,48 @@ func TestDelBounceList(t *testing.T) {
 		return false
 	}
 
-	for i := 0; i < 3; i++ {
-		// Compute an e-mail address for our Bounce.
-		exampleEmail := fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain)
+	createdAt, err := mailgun.NewRFC2822Time("Thu, 13 Oct 2011 18:02:00 UTC")
+	if err != nil {
+		t.Fatalf("invalid time")
+	}
 
-		// Add the bounce for our address.
-		err := mg.AddBounce(ctx, exampleEmail, "550", "TestAddDelBounces-generated error")
-		ensure.Nil(t, err)
+	// Generate a list of bounces
+	bounces := []mailgun.Bounce{
+		{
+			Code:    "550",
+			Address: fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain),
+			Error:   "TestAddDelBounces-generated error",
+		},
+		{
+			Code:      "550",
+			Address:   fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain),
+			Error:     "TestAddDelBounces-generated error",
+			CreatedAt: createdAt,
+		},
+	}
 
-		// Give API some time to refresh cache
-		time.Sleep(time.Second)
+	// Add the bounce for our address.
+	err = mg.AddBounces(ctx, bounces)
+	ensure.Nil(t, err)
 
-		// We should now have one bounce listed when we query the API.
-		if !findBounce(exampleEmail) {
-			t.Fatalf("Expected bounce for address %s in list of bounces", exampleEmail)
+	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)
+		ensure.Nil(t, err)
+		if bounce.Address != expect.Address {
+			t.Fatalf("Expected at least one bounce for %s", expect.Address)
+		}
+		t.Logf("Bounce Created At: %s", bounce.CreatedAt)
+		if !expect.CreatedAt.IsZero() && bounce.CreatedAt != expect.CreatedAt {
+			t.Fatalf("Expected bounce createdAt to be %s, got %s", expect.CreatedAt, bounce.CreatedAt)
 		}
 	}
 
 	// Delete the bounce list.  This should put us back the way we were.
-	err := mg.DeleteBounceList(ctx)
+	err = mg.DeleteBounceList(ctx)
 	ensure.Nil(t, err)
 
 	it := mg.ListBounces(nil)
diff --git a/spam_complaints_test.go b/spam_complaints_test.go
index 5f0c143..eb9cd59 100644
--- a/spam_complaints_test.go
+++ b/spam_complaints_test.go
@@ -67,3 +67,41 @@ func TestCreateDeleteComplaint(t *testing.T) {
 	ensure.Nil(t, mg.DeleteComplaint(ctx, randomMail))
 	ensure.False(t, hasComplaint(randomMail))
 }
+
+func TestCreateDeleteComplaintList(t *testing.T) {
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
+	ctx := context.Background()
+
+	var hasComplaint = func(email string) bool {
+		t.Logf("hasComplaint: %s\n", email)
+		it := mg.ListComplaints(nil)
+		ensure.Nil(t, it.Err())
+
+		var page []mailgun.Complaint
+		for it.Next(ctx, &page) {
+			for _, complaint := range page {
+				t.Logf("Complaint Address: %s\n", complaint.Address)
+				if complaint.Address == email {
+					return true
+				}
+			}
+		}
+		return false
+	}
+
+	addresses := []string{
+		strings.ToLower(randomString(64, "")) + "@example1.com",
+		strings.ToLower(randomString(64, "")) + "@example2.com",
+		strings.ToLower(randomString(64, "")) + "@example3.com",
+	}
+
+	ensure.Nil(t, mg.CreateComplaints(ctx, addresses))
+
+	for _, address := range addresses {
+		ensure.True(t, hasComplaint(address))
+		ensure.Nil(t, mg.DeleteComplaint(ctx, address))
+		ensure.False(t, hasComplaint(address))
+	}
+
+}