203 lines
6.0 KiB
Go
203 lines
6.0 KiB
Go
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"`
|
|
}
|
|
|
|
type bouncesListResponse struct {
|
|
Items []Bounce `json:"items"`
|
|
Paging Paging `json:"paging"`
|
|
}
|
|
|
|
// ListBounces returns a complete set of bounces logged against the sender's domain, if any.
|
|
// 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())
|
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
|
if opts != nil {
|
|
if opts.Limit != 0 {
|
|
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
|
}
|
|
}
|
|
url, err := r.generateUrlWithParameters()
|
|
return &BouncesIterator{
|
|
mg: mg,
|
|
bouncesListResponse: bouncesListResponse{Paging: Paging{Next: url, First: url}},
|
|
err: err,
|
|
}
|
|
}
|
|
|
|
type BouncesIterator struct {
|
|
bouncesListResponse
|
|
mg Mailgun
|
|
err error
|
|
}
|
|
|
|
// 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 {
|
|
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))
|
|
copy(cpy, ci.Items)
|
|
*items = cpy
|
|
if len(ci.Items) == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 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 {
|
|
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))
|
|
copy(cpy, ci.Items)
|
|
*items = cpy
|
|
return true
|
|
}
|
|
|
|
// Last 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 *BouncesIterator) Last(ctx context.Context, items *[]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))
|
|
copy(cpy, ci.Items)
|
|
*items = cpy
|
|
return true
|
|
}
|
|
|
|
// 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 {
|
|
if ci.err != nil {
|
|
return false
|
|
}
|
|
if ci.Paging.Previous == "" {
|
|
return false
|
|
}
|
|
ci.err = ci.fetch(ctx, ci.Paging.Previous)
|
|
if ci.err != nil {
|
|
return false
|
|
}
|
|
cpy := make([]Bounce, len(ci.Items))
|
|
copy(cpy, ci.Items)
|
|
*items = cpy
|
|
if len(ci.Items) == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (ci *BouncesIterator) 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.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())
|
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
|
|
|
var response Bounce
|
|
err := getResponseFromJSON(ctx, r, &response)
|
|
return response, err
|
|
}
|
|
|
|
// AddBounce files a bounce report.
|
|
// Address identifies the intended recipient of the message that bounced.
|
|
// Code corresponds to the numeric response given by the e-mail server which rejected the message.
|
|
// Error providees the corresponding human readable reason for the problem.
|
|
// For example,
|
|
// here's how the these two fields relate.
|
|
// Suppose the SMTP server responds with an error, as below.
|
|
// Then, . . .
|
|
//
|
|
// 550 Requested action not taken: mailbox unavailable
|
|
// \___/\_______________________________________________/
|
|
// | |
|
|
// `-- Code `-- Error
|
|
//
|
|
// 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, error string) error {
|
|
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
|
|
r.setClient(mg.Client())
|
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
|
|
|
payload := newUrlEncodedPayload()
|
|
payload.addValue("address", address)
|
|
if code != "" {
|
|
payload.addValue("code", code)
|
|
}
|
|
if error != "" {
|
|
payload.addValue("error", error)
|
|
}
|
|
_, 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)
|
|
r.setClient(mg.Client())
|
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
|
_, err := makeDeleteRequest(ctx, r)
|
|
return err
|
|
}
|