265 lines
8.1 KiB
Go
265 lines
8.1 KiB
Go
|
package mailgun
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// yes and no are variables which provide us the ability to take their addresses.
|
||
|
// Subscribed and Unsubscribed are pointers to these booleans.
|
||
|
//
|
||
|
// We use a pointer to boolean as a kind of trinary data type:
|
||
|
// if nil, the relevant data type remains unspecified.
|
||
|
// Otherwise, its value is either true or false.
|
||
|
var (
|
||
|
yes bool = true
|
||
|
no bool = false
|
||
|
)
|
||
|
|
||
|
// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
|
||
|
// This attribute may be used to filter the results returned by GetSubscribers().
|
||
|
// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
|
||
|
var (
|
||
|
All *bool = nil
|
||
|
Subscribed *bool = &yes
|
||
|
Unsubscribed *bool = &no
|
||
|
)
|
||
|
|
||
|
// A Member structure represents a member of the mailing list.
|
||
|
// The Vars field can represent any JSON-encodable data.
|
||
|
type Member struct {
|
||
|
Address string `json:"address,omitempty"`
|
||
|
Name string `json:"name,omitempty"`
|
||
|
Subscribed *bool `json:"subscribed,omitempty"`
|
||
|
Vars map[string]interface{} `json:"vars,omitempty"`
|
||
|
}
|
||
|
|
||
|
type memberListResponse struct {
|
||
|
Lists []Member `json:"items"`
|
||
|
Paging Paging `json:"paging"`
|
||
|
}
|
||
|
|
||
|
type memberResponse struct {
|
||
|
Member Member `json:"member"`
|
||
|
}
|
||
|
|
||
|
type MemberListIterator struct {
|
||
|
memberListResponse
|
||
|
mg Mailgun
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
// Used by List methods to specify what list parameters to send to the mailgun API
|
||
|
type ListOptions struct {
|
||
|
Limit int
|
||
|
}
|
||
|
|
||
|
func (mg *MailgunImpl) ListMembers(address string, opts *ListOptions) *MemberListIterator {
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, address) + "/pages")
|
||
|
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 &MemberListIterator{
|
||
|
mg: mg,
|
||
|
memberListResponse: memberListResponse{Paging: Paging{Next: url, First: url}},
|
||
|
err: err,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If an error occurred during iteration `Err()` will return non nil
|
||
|
func (li *MemberListIterator) Err() error {
|
||
|
return li.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 (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool {
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
li.err = li.fetch(ctx, li.Paging.Next)
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
*items = li.Lists
|
||
|
if len(li.Lists) == 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 (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool {
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
li.err = li.fetch(ctx, li.Paging.First)
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
*items = li.Lists
|
||
|
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 (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool {
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
li.err = li.fetch(ctx, li.Paging.Last)
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
*items = li.Lists
|
||
|
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 (li *MemberListIterator) Previous(ctx context.Context, items *[]Member) bool {
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
if li.Paging.Previous == "" {
|
||
|
return false
|
||
|
}
|
||
|
li.err = li.fetch(ctx, li.Paging.Previous)
|
||
|
if li.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
*items = li.Lists
|
||
|
if len(li.Lists) == 0 {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (li *MemberListIterator) fetch(ctx context.Context, url string) error {
|
||
|
r := newHTTPRequest(url)
|
||
|
r.setClient(li.mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, li.mg.APIKey())
|
||
|
|
||
|
return getResponseFromJSON(ctx, r, &li.memberListResponse)
|
||
|
}
|
||
|
|
||
|
// GetMember returns a complete Member structure for a member of a mailing list,
|
||
|
// given only their subscription e-mail address.
|
||
|
func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, error) {
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
|
||
|
r.setClient(mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||
|
response, err := makeGetRequest(ctx, r)
|
||
|
if err != nil {
|
||
|
return Member{}, err
|
||
|
}
|
||
|
var resp memberResponse
|
||
|
err = response.parseFromJSON(&resp)
|
||
|
return resp.Member, err
|
||
|
}
|
||
|
|
||
|
// CreateMember registers a new member of the indicated mailing list.
|
||
|
// If merge is set to true, then the registration may update an existing Member's settings.
|
||
|
// Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address.
|
||
|
func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error {
|
||
|
vs, err := json.Marshal(prototype.Vars)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr))
|
||
|
r.setClient(mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||
|
p := newFormDataPayload()
|
||
|
p.addValue("upsert", yesNo(merge))
|
||
|
p.addValue("address", prototype.Address)
|
||
|
p.addValue("name", prototype.Name)
|
||
|
p.addValue("vars", string(vs))
|
||
|
if prototype.Subscribed != nil {
|
||
|
p.addValue("subscribed", yesNo(*prototype.Subscribed))
|
||
|
}
|
||
|
_, err = makePostRequest(ctx, r, p)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// UpdateMember lets you change certain details about the indicated mailing list member.
|
||
|
// Address, Name, Vars, and Subscribed fields may be changed.
|
||
|
func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype Member) (Member, error) {
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
|
||
|
r.setClient(mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||
|
p := newFormDataPayload()
|
||
|
if prototype.Address != "" {
|
||
|
p.addValue("address", prototype.Address)
|
||
|
}
|
||
|
if prototype.Name != "" {
|
||
|
p.addValue("name", prototype.Name)
|
||
|
}
|
||
|
if prototype.Vars != nil {
|
||
|
vs, err := json.Marshal(prototype.Vars)
|
||
|
if err != nil {
|
||
|
return Member{}, err
|
||
|
}
|
||
|
p.addValue("vars", string(vs))
|
||
|
}
|
||
|
if prototype.Subscribed != nil {
|
||
|
p.addValue("subscribed", yesNo(*prototype.Subscribed))
|
||
|
}
|
||
|
response, err := makePutRequest(ctx, r, p)
|
||
|
if err != nil {
|
||
|
return Member{}, err
|
||
|
}
|
||
|
var envelope struct {
|
||
|
Member Member `json:"member"`
|
||
|
}
|
||
|
err = response.parseFromJSON(&envelope)
|
||
|
return envelope.Member, err
|
||
|
}
|
||
|
|
||
|
// DeleteMember removes the member from the list.
|
||
|
func (mg *MailgunImpl) DeleteMember(ctx context.Context, member, addr string) error {
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + "/" + member)
|
||
|
r.setClient(mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||
|
_, err := makeDeleteRequest(ctx, r)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// CreateMemberList registers multiple Members and non-Member members to a single mailing list
|
||
|
// in a single round-trip.
|
||
|
// u indicates if the existing members should be updated or duplicates should be updated.
|
||
|
// Use All to elect not to provide a default.
|
||
|
// The newMembers list can take one of two JSON-encodable forms: an slice of strings, or
|
||
|
// a slice of Member structures.
|
||
|
// If a simple slice of strings is passed, each string refers to the member's e-mail address.
|
||
|
// Otherwise, each Member needs to have at least the Address field filled out.
|
||
|
// Other fields are optional, but may be set according to your needs.
|
||
|
func (mg *MailgunImpl) CreateMemberList(ctx context.Context, u *bool, addr string, newMembers []interface{}) error {
|
||
|
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + ".json")
|
||
|
r.setClient(mg.Client())
|
||
|
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||
|
p := newFormDataPayload()
|
||
|
if u != nil {
|
||
|
p.addValue("upsert", yesNo(*u))
|
||
|
}
|
||
|
bs, err := json.Marshal(newMembers)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
p.addValue("members", string(bs))
|
||
|
_, err = makePostRequest(ctx, r, p)
|
||
|
return err
|
||
|
}
|