Skip to content

Commit

Permalink
CreateDomain now accepts CreateDomainOption{}
Browse files Browse the repository at this point in the history
* CreateDomain() now supports all optional parameters not just
spam_action and wildcard.
* ListComplaints() now returns a page iterator
* Renamed TagItem to Tag
  • Loading branch information
thrawn01 committed Jan 10, 2019
1 parent 7d22f9e commit e49862a
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 57 deletions.
45 changes: 36 additions & 9 deletions domains.go
Expand Up @@ -3,6 +3,7 @@ package mailgun
import (
"context"
"strconv"
"strings"
"time"
)

Expand All @@ -17,11 +18,13 @@ const (
// Tag instruments the received message with headers providing a measure of its spamness.
// Delete instructs Mailgun to just block or delete the message all-together.
const (
SpamActionTag = "tag"
SpamActionDisabled = "disabled"
SpamActionDelete = "delete"
SpamActionTag = SpamAction("tag")
SpamActionDisabled = SpamAction("disabled")
SpamActionDelete = SpamAction("delete")
)

type SpamAction string

// A Domain structure holds information about a domain used when sending mail.
type Domain struct {
CreatedAt string `json:"created_at"`
Expand All @@ -30,8 +33,8 @@ type Domain struct {
SMTPPassword string `json:"smtp_password"`
Wildcard bool `json:"wildcard"`
// The SpamAction field must be one of Tag, Disabled, or Delete.
SpamAction string `json:"spam_action"`
State string `json:"state"`
SpamAction string `json:"spam_action"`
State string `json:"state"`
}

// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun.
Expand Down Expand Up @@ -123,22 +126,46 @@ func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string) (Domain, []
return resp.Domain, resp.ReceivingDNSRecords, resp.SendingDNSRecords, err
}

type CreateDomainOptions struct {
SpamAction SpamAction
Wildcard bool
ForceDKIMAuthority bool
DKIMKeySize int
IPS []string
}

// CreateDomain instructs Mailgun to create a new domain for your account.
// The name parameter identifies the domain.
// The smtpPassword parameter provides an access credential for the domain.
// 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, smtpPassword string, spamAction string, wildcard bool) error {
func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, password string, opts *CreateDomainOptions) error {
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.APIKey())

payload := newUrlEncodedPayload()
payload.addValue("name", name)
payload.addValue("smtp_password", smtpPassword)
payload.addValue("spam_action", spamAction)
payload.addValue("wildcard", strconv.FormatBool(wildcard))
payload.addValue("smtp_password", password)

if opts != nil {
if opts.SpamAction != "" {
payload.addValue("spam_action", string(opts.SpamAction))
}
if opts.Wildcard {
payload.addValue("wildcard", boolToString(opts.Wildcard))
}
if opts.ForceDKIMAuthority {
payload.addValue("force_dkim_authority", boolToString(opts.ForceDKIMAuthority))
}
if opts.DKIMKeySize != 0 {
payload.addValue("dkim_key_size", strconv.Itoa(opts.DKIMKeySize))
}
if len(opts.IPS) != 0 {
payload.addValue("ips", strings.Join(opts.IPS, ","))
}
}
_, err := makePostRequest(ctx, r, payload)
return err
}
Expand Down
3 changes: 2 additions & 1 deletion domains_test.go
Expand Up @@ -71,7 +71,8 @@ func TestAddDeleteDomain(t *testing.T) {
ctx := context.Background()

// First, we need to add the domain.
ensure.Nil(t, mg.CreateDomain(ctx, "mx.mailgun.test", "supersecret", mailgun.SpamActionTag, false))
ensure.Nil(t, mg.CreateDomain(ctx, "mx.mailgun.test", "supersecret",
&mailgun.CreateDomainOptions{SpamAction: mailgun.SpamActionTag}))
// Next, we delete it.
ensure.Nil(t, mg.DeleteDomain(ctx, "mx.mailgun.test"))
}
Expand Down
6 changes: 3 additions & 3 deletions mailgun.go
Expand Up @@ -154,13 +154,13 @@ type Mailgun interface {
DeleteBounce(ctx context.Context, address string) error

ListStats(ctx context.Context, events []string, opts *ListStatOptions) ([]Stats, error)
GetTag(ctx context.Context, tag string) (TagItem, error)
GetTag(ctx context.Context, tag string) (Tag, error)
DeleteTag(ctx context.Context, tag string) error
ListTags(*ListTagOptions) *TagIterator

ListDomains(ctx context.Context, opts *ListOptions) (int, []Domain, error)
GetDomain(ctx context.Context, domain string) (Domain, []DNSRecord, []DNSRecord, error)
CreateDomain(ctx context.Context, name string, smtpPassword string, spamAction string, wildcard bool) error
CreateDomain(ctx context.Context, name string, pass string, opts *CreateDomainOptions) error
DeleteDomain(ctx context.Context, name string) error
UpdateDomainConnection(ctx context.Context, domain string, dc DomainConnection) error
GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error)
Expand All @@ -183,7 +183,7 @@ type Mailgun interface {
DeleteUnsubscribe(ctx context.Context, address string) error
DeleteUnsubscribeWithTag(ctx context.Context, a, t string) error

ListComplaints(ctx context.Context, opts *ListOptions) ([]Complaint, error)
ListComplaints(opts *ListOptions) *ComplaintsIterator
GetComplaint(ctx context.Context, address string) (Complaint, error)
CreateComplaint(ctx context.Context, address string) error
DeleteComplaint(ctx context.Context, address string) error
Expand Down
16 changes: 12 additions & 4 deletions mailing_lists.go
Expand Up @@ -79,7 +79,9 @@ func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool {
if li.err != nil {
return false
}
*items = li.Items
cpy := make([]MailingList, len(li.Items))
copy(cpy, li.Items)
*items = cpy
if len(li.Items) == 0 {
return false
}
Expand All @@ -97,7 +99,9 @@ func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool {
if li.err != nil {
return false
}
*items = li.Items
cpy := make([]MailingList, len(li.Items))
copy(cpy, li.Items)
*items = cpy
return true
}

Expand All @@ -113,7 +117,9 @@ func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool {
if li.err != nil {
return false
}
*items = li.Items
cpy := make([]MailingList, len(li.Items))
copy(cpy, li.Items)
*items = cpy
return true
}

Expand All @@ -131,7 +137,9 @@ func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) boo
if li.err != nil {
return false
}
*items = li.Items
cpy := make([]MailingList, len(li.Items))
copy(cpy, li.Items)
*items = cpy
if len(li.Items) == 0 {
return false
}
Expand Down
5 changes: 2 additions & 3 deletions routes.go
Expand Up @@ -180,6 +180,8 @@ func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool {

func (ri *RoutesIterator) fetch(ctx context.Context, skip, limit int) error {
r := newHTTPRequest(ri.url)
r.setBasicAuth(basicAuthUser, ri.mg.APIKey())
r.setClient(ri.mg.Client())

if skip != 0 {
r.addParameter("skip", strconv.Itoa(skip))
Expand All @@ -188,9 +190,6 @@ func (ri *RoutesIterator) fetch(ctx context.Context, skip, limit int) error {
r.addParameter("limit", strconv.Itoa(limit))
}

r.setClient(ri.mg.Client())
r.setBasicAuth(basicAuthUser, ri.mg.APIKey())

return getResponseFromJSON(ctx, r, &ri.routesListResponse)
}

Expand Down
119 changes: 106 additions & 13 deletions spam_complaints.go
Expand Up @@ -19,33 +19,126 @@ type Complaint struct {
Address string `json:"address"`
}

type complaintsEnvelope struct {
Items []Complaint `json:"items"`
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.
func (mg *MailgunImpl) ListComplaints(ctx context.Context, opts *ListOptions) ([]Complaint, error) {
r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint))
func (mg *MailgunImpl) ListComplaints(opts *ListOptions) *ComplaintsIterator {
r := newHTTPRequest(generatePublicApiUrl(mg, mg.domain+"/"+complaintsEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.APIKey())
if opts != nil {
if opts.Limit != 0 {
r.addParameter("limit", strconv.Itoa(opts.Limit))
}
}
url, err := r.generateUrlWithParameters()
return &ComplaintsIterator{
mg: mg,
complaintsResponse: complaintsResponse{Paging: Paging{Next: url, First: url}},
err: err,
}
}

if opts != nil && opts.Limit != 0 {
r.addParameter("limit", strconv.Itoa(opts.Limit))
type ComplaintsIterator struct {
complaintsResponse
mg Mailgun
err error
}

// If an error occurred during iteration `Err()` will return non nil
func (ci *ComplaintsIterator) Err() error {
return ci.err
}

// 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 {
if ci.err != nil {
return false
}
ci.err = ci.fetch(ctx, ci.Paging.Next)
if ci.err != nil {
return false
}
cpy := make([]Complaint, len(ci.Items))
copy(cpy, ci.Items)
*items = cpy
if len(ci.Items) == 0 {
return false
}
return true
}

if opts != nil && opts.Skip != 0 {
r.addParameter("skip", strconv.Itoa(opts.Skip))
// 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 {
if ci.err != nil {
return false
}
ci.err = ci.fetch(ctx, ci.Paging.First)
if ci.err != nil {
return false
}
cpy := make([]Complaint, len(ci.Items))
copy(cpy, ci.Items)
*items = cpy
return true
}

// Retrieves the last page of items from the api.
// 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 {
if ci.err != nil {
return false
}
ci.err = ci.fetch(ctx, ci.Paging.Last)
if ci.err != nil {
return false
}
cpy := make([]Complaint, len(ci.Items))
copy(cpy, ci.Items)
*items = cpy
return true
}

var envelope complaintsEnvelope
err := getResponseFromJSON(ctx, r, &envelope)
if err != nil {
return nil, err
// 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 {
if ci.err != nil {
return false
}
return envelope.Items, nil
if ci.Paging.Previous == "" {
return false
}
ci.err = ci.fetch(ctx, ci.Paging.Previous)
if ci.err != nil {
return false
}
cpy := make([]Complaint, len(ci.Items))
copy(cpy, ci.Items)
*items = cpy
if len(ci.Items) == 0 {
return false
}
return true
}

func (ci *ComplaintsIterator) fetch(ctx context.Context, url string) error {
r := newHTTPRequest(url)
r.setClient(ci.mg.Client())
r.setBasicAuth(basicAuthUser, ci.mg.APIKey())

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

// GetComplaint returns a single complaint record filed by a recipient at the email address provided.
Expand Down
20 changes: 14 additions & 6 deletions spam_complaints_test.go
Expand Up @@ -18,7 +18,11 @@ func TestGetComplaints(t *testing.T) {
ensure.Nil(t, err)
ctx := context.Background()

_, err = mg.ListComplaints(ctx, nil)
it := mg.ListComplaints(nil)
var page []Complaint
for it.Next(ctx, &page) {
//spew.Dump(page)
}
ensure.Nil(t, err)
}

Expand All @@ -40,6 +44,7 @@ func TestGetComplaintFromRandomNoComplaint(t *testing.T) {
}

func TestCreateDeleteComplaint(t *testing.T) {
Debug = true
if reason := SkipNetworkTest(); reason != "" {
t.Skip(reason)
}
Expand All @@ -50,13 +55,16 @@ func TestCreateDeleteComplaint(t *testing.T) {

var hasComplaint = func(email string) bool {
t.Logf("hasComplaint: %s\n", email)
complaints, err := mg.ListComplaints(ctx, nil)
it := mg.ListComplaints(nil)
ensure.Nil(t, err)

for _, complaint := range complaints {
t.Logf("Complaint Address: %s\n", complaint.Address)
if complaint.Address == email {
return true
var page []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
Expand Down

0 comments on commit e49862a

Please sign in to comment.