This commit is contained in:
Eric Chiang 2016-06-28 10:18:18 -07:00
parent abd36d637c
commit 9fe70514ab
33 changed files with 628 additions and 1105 deletions

10
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: af5ee807138ecd53eebc54567b4dcd66e3a6ddc7d758fb9f34e6e3eb13b20cf2 hash: ed84ba1c371faf2f9a0bf64f6a7767a017e663cb99afa273ec5659432bb474d5
updated: 2016-06-13T10:58:04.608971954+09:00 updated: 2016-06-28T10:15:56.223659517-07:00
imports: imports:
- name: github.com/andybalholm/cascadia - name: github.com/andybalholm/cascadia
version: 6122e68c2642b7b75c538a63b15168c6c80fb757 version: 6122e68c2642b7b75c538a63b15168c6c80fb757
@ -39,11 +39,9 @@ imports:
subpackages: subpackages:
- oid - oid
- name: github.com/mailgun/mailgun-go - name: github.com/mailgun/mailgun-go
version: 9578dc67692294bb7e2a6f4b15dd18c97af19440 version: a98c4eecda3e6a36bd804ae80e9a4f8edaa6cd99
- name: github.com/mattn/go-sqlite3 - name: github.com/mattn/go-sqlite3
version: 2513631704612107a1c8b1803fb8e6b3dda2230e version: 2513631704612107a1c8b1803fb8e6b3dda2230e
- name: github.com/mbanzon/simplehttp
version: 04c542e7ac706a25820090f274ea6a4f39a63326
- name: github.com/pborman/uuid - name: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- name: github.com/PuerkitoBio/goquery - name: github.com/PuerkitoBio/goquery
@ -83,4 +81,4 @@ imports:
version: c87af80f3cc5036b55b83d77171e156791085e2e version: c87af80f3cc5036b55b83d77171e156791085e2e
- name: gopkg.in/ldap.v2 - name: gopkg.in/ldap.v2
version: e9a325d64989e2844be629682cb085d2c58eef8d version: e9a325d64989e2844be629682cb085d2c58eef8d
devImports: [] testImports: []

View file

@ -1,8 +1,7 @@
language: go language: go
go: go:
- 1.1.2 - 1.4
- 1.2 - 1.3.3
- tip
env: env:
- GOARCH=amd64 - GOARCH=amd64
- GOARCH=386 - GOARCH=386

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
"time" "time"
) )
@ -52,15 +51,16 @@ func (b Bounce) GetCode() (int, error) {
// and the slice of bounces specified, if successful. // and the slice of bounces specified, if successful.
// Note that the length of the slice may be smaller than the total number of bounces. // Note that the length of the slice may be smaller than the total number of bounces.
func (m *MailgunImpl) GetBounces(limit, skip int) (int, []Bounce, error) { func (m *MailgunImpl) GetBounces(limit, skip int) (int, []Bounce, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint)) r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint))
if limit != -1 { if limit != -1 {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != -1 { if skip != -1 {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var response bounceEnvelope var response bounceEnvelope
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
@ -73,8 +73,9 @@ func (m *MailgunImpl) GetBounces(limit, skip int) (int, []Bounce, error) {
// GetSingleBounce retrieves a single bounce record, if any exist, for the given recipient address. // GetSingleBounce retrieves a single bounce record, if any exist, for the given recipient address.
func (m *MailgunImpl) GetSingleBounce(address string) (Bounce, error) { func (m *MailgunImpl) GetSingleBounce(address string) (Bounce, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address) r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var response singleBounceEnvelope var response singleBounceEnvelope
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
@ -98,16 +99,17 @@ func (m *MailgunImpl) GetSingleBounce(address string) (Bounce, error) {
// Note that both code and error exist as strings, even though // Note that both code and error exist as strings, even though
// code will report as a number. // code will report as a number.
func (m *MailgunImpl) AddBounce(address, code, error string) error { func (m *MailgunImpl) AddBounce(address, code, error string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint)) r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload() payload := newUrlEncodedPayload()
payload.AddValue("address", address) payload.addValue("address", address)
if code != "" { if code != "" {
payload.AddValue("code", code) payload.addValue("code", code)
} }
if error != "" { if error != "" {
payload.AddValue("error", error) payload.addValue("error", error)
} }
_, err := makePostRequest(r, payload) _, err := makePostRequest(r, payload)
return err return err
@ -115,8 +117,9 @@ func (m *MailgunImpl) AddBounce(address, code, error string) error {
// DeleteBounce removes all bounces associted with the provided e-mail address. // DeleteBounce removes all bounces associted with the provided e-mail address.
func (m *MailgunImpl) DeleteBounce(address string) error { func (m *MailgunImpl) DeleteBounce(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address) r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,9 +1,5 @@
package mailgun package mailgun
import (
"github.com/mbanzon/simplehttp"
)
// Campaigns have been deprecated since development work on this SDK commenced. // Campaigns have been deprecated since development work on this SDK commenced.
// Please refer to http://documentation.mailgun.com/api_reference . // Please refer to http://documentation.mailgun.com/api_reference .
type Campaign struct { type Campaign struct {
@ -28,8 +24,9 @@ type campaignsEnvelope struct {
// Campaigns have been deprecated since development work on this SDK commenced. // Campaigns have been deprecated since development work on this SDK commenced.
// Please refer to http://documentation.mailgun.com/api_reference . // Please refer to http://documentation.mailgun.com/api_reference .
func (m *MailgunImpl) GetCampaigns() (int, []Campaign, error) { func (m *MailgunImpl) GetCampaigns() (int, []Campaign, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, campaignsEndpoint)) r := newHTTPRequest(generateApiUrl(m, campaignsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var envelope campaignsEnvelope var envelope campaignsEnvelope
err := getResponseFromJSON(r, &envelope) err := getResponseFromJSON(r, &envelope)
@ -42,13 +39,14 @@ func (m *MailgunImpl) GetCampaigns() (int, []Campaign, error) {
// Campaigns have been deprecated since development work on this SDK commenced. // Campaigns have been deprecated since development work on this SDK commenced.
// Please refer to http://documentation.mailgun.com/api_reference . // Please refer to http://documentation.mailgun.com/api_reference .
func (m *MailgunImpl) CreateCampaign(name, id string) error { func (m *MailgunImpl) CreateCampaign(name, id string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, campaignsEndpoint)) r := newHTTPRequest(generateApiUrl(m, campaignsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload() payload := newUrlEncodedPayload()
payload.AddValue("name", name) payload.addValue("name", name)
if id != "" { if id != "" {
payload.AddValue("id", id) payload.addValue("id", id)
} }
_, err := makePostRequest(r, payload) _, err := makePostRequest(r, payload)
return err return err
@ -57,13 +55,14 @@ func (m *MailgunImpl) CreateCampaign(name, id string) error {
// Campaigns have been deprecated since development work on this SDK commenced. // Campaigns have been deprecated since development work on this SDK commenced.
// Please refer to http://documentation.mailgun.com/api_reference . // Please refer to http://documentation.mailgun.com/api_reference .
func (m *MailgunImpl) UpdateCampaign(oldId, name, newId string) error { func (m *MailgunImpl) UpdateCampaign(oldId, name, newId string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, campaignsEndpoint) + "/" + oldId) r := newHTTPRequest(generateApiUrl(m, campaignsEndpoint) + "/" + oldId)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload() payload := newUrlEncodedPayload()
payload.AddValue("name", name) payload.addValue("name", name)
if newId != "" { if newId != "" {
payload.AddValue("id", newId) payload.addValue("id", newId)
} }
_, err := makePostRequest(r, payload) _, err := makePostRequest(r, payload)
return err return err
@ -72,8 +71,9 @@ func (m *MailgunImpl) UpdateCampaign(oldId, name, newId string) error {
// Campaigns have been deprecated since development work on this SDK commenced. // Campaigns have been deprecated since development work on this SDK commenced.
// Please refer to http://documentation.mailgun.com/api_reference . // Please refer to http://documentation.mailgun.com/api_reference .
func (m *MailgunImpl) DeleteCampaign(id string) error { func (m *MailgunImpl) DeleteCampaign(id string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, campaignsEndpoint) + "/" + id) r := newHTTPRequest(generateApiUrl(m, campaignsEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -2,7 +2,6 @@ package mailgun
import ( import (
"fmt" "fmt"
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
) )
@ -18,14 +17,15 @@ var ErrEmptyParam = fmt.Errorf("empty or illegal parameter")
// GetCredentials returns the (possibly zero-length) list of credentials associated with your domain. // GetCredentials returns the (possibly zero-length) list of credentials associated with your domain.
func (mg *MailgunImpl) GetCredentials(limit, skip int) (int, []Credential, error) { func (mg *MailgunImpl) GetCredentials(limit, skip int) (int, []Credential, error) {
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, "")) r := newHTTPRequest(generateCredentialsUrl(mg, ""))
r.setClient(mg.Client())
if limit != DefaultLimit { if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
Items []Credential `json:"items"` Items []Credential `json:"items"`
@ -42,11 +42,12 @@ func (mg *MailgunImpl) CreateCredential(login, password string) error {
if (login == "") || (password == "") { if (login == "") || (password == "") {
return ErrEmptyParam return ErrEmptyParam
} }
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, "")) r := newHTTPRequest(generateCredentialsUrl(mg, ""))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("login", login) p := newUrlEncodedPayload()
p.AddValue("password", password) p.addValue("login", login)
p.addValue("password", password)
_, err := makePostRequest(r, p) _, err := makePostRequest(r, p)
return err return err
} }
@ -56,10 +57,11 @@ func (mg *MailgunImpl) ChangeCredentialPassword(id, password string) error {
if (id == "") || (password == "") { if (id == "") || (password == "") {
return ErrEmptyParam return ErrEmptyParam
} }
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, id)) r := newHTTPRequest(generateCredentialsUrl(mg, id))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("password", password) p := newUrlEncodedPayload()
p.addValue("password", password)
_, err := makePutRequest(r, p) _, err := makePutRequest(r, p)
return err return err
} }
@ -69,8 +71,9 @@ func (mg *MailgunImpl) DeleteCredential(id string) error {
if id == "" { if id == "" {
return ErrEmptyParam return ErrEmptyParam
} }
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, id)) r := newHTTPRequest(generateCredentialsUrl(mg, id))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
"time" "time"
) )
@ -67,14 +66,15 @@ func (d Domain) GetCreatedAt() (t time.Time, err error) {
// Note that zero items and a zero-length slice do not necessarily imply an error occurred. // Note that zero items and a zero-length slice do not necessarily imply an error occurred.
// Except for the error itself, all results are undefined in the event of an error. // Except for the error itself, all results are undefined in the event of an error.
func (m *MailgunImpl) GetDomains(limit, skip int) (int, []Domain, error) { func (m *MailgunImpl) GetDomains(limit, skip int) (int, []Domain, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint)) r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r.setClient(m.Client())
if limit != DefaultLimit { if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setBasicAuth(basicAuthUser, m.ApiKey())
var envelope domainsEnvelope var envelope domainsEnvelope
err := getResponseFromJSON(r, &envelope) err := getResponseFromJSON(r, &envelope)
@ -86,8 +86,9 @@ func (m *MailgunImpl) GetDomains(limit, skip int) (int, []Domain, error) {
// Retrieve detailed information about the named domain. // Retrieve detailed information about the named domain.
func (m *MailgunImpl) GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error) { func (m *MailgunImpl) GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + domain) r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + domain)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var envelope singleDomainEnvelope var envelope singleDomainEnvelope
err := getResponseFromJSON(r, &envelope) err := getResponseFromJSON(r, &envelope)
return envelope.Domain, envelope.ReceivingDNSRecords, envelope.SendingDNSRecords, err return envelope.Domain, envelope.ReceivingDNSRecords, envelope.SendingDNSRecords, err
@ -100,22 +101,24 @@ func (m *MailgunImpl) GetSingleDomain(domain string) (Domain, []DNSRecord, []DNS
// The wildcard parameter instructs Mailgun to treat all subdomains of this domain uniformly if true, // The wildcard parameter instructs Mailgun to treat all subdomains of this domain uniformly if true,
// and as different domains if false. // and as different domains if false.
func (m *MailgunImpl) CreateDomain(name string, smtpPassword string, spamAction string, wildcard bool) error { func (m *MailgunImpl) CreateDomain(name string, smtpPassword string, spamAction string, wildcard bool) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint)) r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload() payload := newUrlEncodedPayload()
payload.AddValue("name", name) payload.addValue("name", name)
payload.AddValue("smtp_password", smtpPassword) payload.addValue("smtp_password", smtpPassword)
payload.AddValue("spam_action", spamAction) payload.addValue("spam_action", spamAction)
payload.AddValue("wildcard", strconv.FormatBool(wildcard)) payload.addValue("wildcard", strconv.FormatBool(wildcard))
_, err := makePostRequest(r, payload) _, err := makePostRequest(r, payload)
return err return err
} }
// DeleteDomain instructs Mailgun to dispose of the named domain name. // DeleteDomain instructs Mailgun to dispose of the named domain name.
func (m *MailgunImpl) DeleteDomain(name string) error { func (m *MailgunImpl) DeleteDomain(name string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + name) r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + name)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strings" "strings"
) )
@ -41,9 +40,10 @@ type addressParseResult struct {
// It may also be used to break an email address into its sub-components. (See example.) // It may also be used to break an email address into its sub-components. (See example.)
// NOTE: Use of this function requires a proper public API key. The private API key will not work. // NOTE: Use of this function requires a proper public API key. The private API key will not work.
func (m *MailgunImpl) ValidateEmail(email string) (EmailVerification, error) { func (m *MailgunImpl) ValidateEmail(email string) (EmailVerification, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(addressValidateEndpoint)) r := newHTTPRequest(generatePublicApiUrl(addressValidateEndpoint))
r.AddParameter("address", email) r.setClient(m.Client())
r.SetBasicAuth(basicAuthUser, m.PublicApiKey()) r.addParameter("address", email)
r.setBasicAuth(basicAuthUser, m.PublicApiKey())
var response EmailVerification var response EmailVerification
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
@ -57,9 +57,10 @@ func (m *MailgunImpl) ValidateEmail(email string) (EmailVerification, error) {
// ParseAddresses takes a list of addresses and sorts them into valid and invalid address categories. // ParseAddresses takes a list of addresses and sorts them into valid and invalid address categories.
// NOTE: Use of this function requires a proper public API key. The private API key will not work. // NOTE: Use of this function requires a proper public API key. The private API key will not work.
func (m *MailgunImpl) ParseAddresses(addresses ...string) ([]string, []string, error) { func (m *MailgunImpl) ParseAddresses(addresses ...string) ([]string, []string, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(addressParseEndpoint)) r := newHTTPRequest(generatePublicApiUrl(addressParseEndpoint))
r.AddParameter("addresses", strings.Join(addresses, ",")) r.setClient(m.Client())
r.SetBasicAuth(basicAuthUser, m.PublicApiKey()) r.addParameter("addresses", strings.Join(addresses, ","))
r.setBasicAuth(basicAuthUser, m.PublicApiKey())
var response addressParseResult var response addressParseResult
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)

View file

@ -2,7 +2,6 @@ package mailgun
import ( import (
"fmt" "fmt"
"github.com/mbanzon/simplehttp"
"time" "time"
) )
@ -58,28 +57,28 @@ func (ei *EventIterator) GetFirstPage(opts GetEventsOptions) error {
return fmt.Errorf("collation cannot at once be both ascending and descending") return fmt.Errorf("collation cannot at once be both ascending and descending")
} }
payload := simplehttp.NewUrlEncodedPayload() payload := newUrlEncodedPayload()
if opts.Limit != 0 { if opts.Limit != 0 {
payload.AddValue("limit", fmt.Sprintf("%d", opts.Limit)) payload.addValue("limit", fmt.Sprintf("%d", opts.Limit))
} }
if opts.Compact { if opts.Compact {
payload.AddValue("pretty", "no") payload.addValue("pretty", "no")
} }
if opts.ForceAscending { if opts.ForceAscending {
payload.AddValue("ascending", "yes") payload.addValue("ascending", "yes")
} }
if opts.ForceDescending { if opts.ForceDescending {
payload.AddValue("ascending", "no") payload.addValue("ascending", "no")
} }
if opts.Begin != noTime { if opts.Begin != noTime {
payload.AddValue("begin", formatMailgunTime(&opts.Begin)) payload.addValue("begin", formatMailgunTime(&opts.Begin))
} }
if opts.End != noTime { if opts.End != noTime {
payload.AddValue("end", formatMailgunTime(&opts.End)) payload.addValue("end", formatMailgunTime(&opts.End))
} }
if opts.Filter != nil { if opts.Filter != nil {
for k, v := range opts.Filter { for k, v := range opts.Filter {
payload.AddValue(k, v) payload.addValue(k, v)
} }
} }
@ -105,8 +104,9 @@ func (ei *EventIterator) GetNext() error {
// GetFirstPage, GetPrevious, and GetNext all have a common body of code. // GetFirstPage, GetPrevious, and GetNext all have a common body of code.
// fetch completes the API fetch common to all three of these functions. // fetch completes the API fetch common to all three of these functions.
func (ei *EventIterator) fetch(url string) error { func (ei *EventIterator) fetch(url string) error {
r := simplehttp.NewHTTPRequest(url) r := newHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, ei.mg.ApiKey()) r.setClient(ei.mg.Client())
r.setBasicAuth(basicAuthUser, ei.mg.ApiKey())
var response map[string]interface{} var response map[string]interface{}
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
if err != nil { if err != nil {

254
vendor/github.com/mailgun/mailgun-go/httphelpers.go generated vendored Normal file
View file

@ -0,0 +1,254 @@
package mailgun
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path"
)
type httpRequest struct {
URL string
Parameters map[string][]string
Headers map[string]string
BasicAuthUser string
BasicAuthPassword string
Client *http.Client
}
type httpResponse struct {
Code int
Data []byte
}
type payload interface {
getPayloadBuffer() (*bytes.Buffer, error)
getContentType() string
}
type keyValuePair struct {
key string
value string
}
type keyNameRC struct {
key string
name string
value io.ReadCloser
}
type formDataPayload struct {
contentType string
Values []keyValuePair
Files []keyValuePair
ReadClosers []keyNameRC
}
type urlEncodedPayload struct {
Values []keyValuePair
}
func newHTTPRequest(url string) *httpRequest {
return &httpRequest{URL: url, Client: http.DefaultClient}
}
func (r *httpRequest) addParameter(name, value string) {
if r.Parameters == nil {
r.Parameters = make(map[string][]string)
}
r.Parameters[name] = append(r.Parameters[name], value)
}
func (r *httpRequest) setClient(c *http.Client) {
r.Client = c
}
func (r *httpRequest) setBasicAuth(user, password string) {
r.BasicAuthUser = user
r.BasicAuthPassword = password
}
func newUrlEncodedPayload() *urlEncodedPayload {
return &urlEncodedPayload{}
}
func (f *urlEncodedPayload) addValue(key, value string) {
f.Values = append(f.Values, keyValuePair{key: key, value: value})
}
func (f *urlEncodedPayload) getPayloadBuffer() (*bytes.Buffer, error) {
data := url.Values{}
for _, keyVal := range f.Values {
data.Add(keyVal.key, keyVal.value)
}
return bytes.NewBufferString(data.Encode()), nil
}
func (f *urlEncodedPayload) getContentType() string {
return "application/x-www-form-urlencoded"
}
func (r *httpResponse) parseFromJSON(v interface{}) error {
return json.Unmarshal(r.Data, v)
}
func newFormDataPayload() *formDataPayload {
return &formDataPayload{}
}
func (f *formDataPayload) addValue(key, value string) {
f.Values = append(f.Values, keyValuePair{key: key, value: value})
}
func (f *formDataPayload) addFile(key, file string) {
f.Files = append(f.Files, keyValuePair{key: key, value: file})
}
func (f *formDataPayload) addReadCloser(key, name string, rc io.ReadCloser) {
f.ReadClosers = append(f.ReadClosers, keyNameRC{key: key, name: name, value: rc})
}
func (f *formDataPayload) getPayloadBuffer() (*bytes.Buffer, error) {
data := &bytes.Buffer{}
writer := multipart.NewWriter(data)
defer writer.Close()
for _, keyVal := range f.Values {
if tmp, err := writer.CreateFormField(keyVal.key); err == nil {
tmp.Write([]byte(keyVal.value))
} else {
return nil, err
}
}
for _, file := range f.Files {
if tmp, err := writer.CreateFormFile(file.key, path.Base(file.value)); err == nil {
if fp, err := os.Open(file.value); err == nil {
defer fp.Close()
io.Copy(tmp, fp)
} else {
return nil, err
}
} else {
return nil, err
}
}
for _, file := range f.ReadClosers {
if tmp, err := writer.CreateFormFile(file.key, file.name); err == nil {
defer file.value.Close()
io.Copy(tmp, file.value)
} else {
return nil, err
}
}
f.contentType = writer.FormDataContentType()
return data, nil
}
func (f *formDataPayload) getContentType() string {
if f.contentType == "" {
f.getPayloadBuffer()
}
return f.contentType
}
func (r *httpRequest) addHeader(name, value string) {
if r.Headers == nil {
r.Headers = make(map[string]string)
}
r.Headers[name] = value
}
func (r *httpRequest) makeGetRequest() (*httpResponse, error) {
return r.makeRequest("GET", nil)
}
func (r *httpRequest) makePostRequest(payload payload) (*httpResponse, error) {
return r.makeRequest("POST", payload)
}
func (r *httpRequest) makePutRequest(payload payload) (*httpResponse, error) {
return r.makeRequest("PUT", payload)
}
func (r *httpRequest) makeDeleteRequest() (*httpResponse, error) {
return r.makeRequest("DELETE", nil)
}
func (r *httpRequest) makeRequest(method string, payload payload) (*httpResponse, error) {
url, err := r.generateUrlWithParameters()
if err != nil {
return nil, err
}
var body io.Reader
if payload != nil {
if body, err = payload.getPayloadBuffer(); err != nil {
return nil, err
}
} else {
body = nil
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
if payload != nil && payload.getContentType() != "" {
req.Header.Add("Content-Type", payload.getContentType())
}
if r.BasicAuthUser != "" && r.BasicAuthPassword != "" {
req.SetBasicAuth(r.BasicAuthUser, r.BasicAuthPassword)
}
for header, value := range r.Headers {
req.Header.Add(header, value)
}
response := httpResponse{}
resp, err := r.Client.Do(req)
if resp != nil {
response.Code = resp.StatusCode
}
if err != nil {
return nil, err
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
response.Data = responseBody
return &response, nil
}
func (r *httpRequest) generateUrlWithParameters() (string, error) {
url, err := url.Parse(r.URL)
if err != nil {
return "", err
}
q := url.Query()
if r.Parameters != nil && len(r.Parameters) > 0 {
for name, values := range r.Parameters {
for _, value := range values {
q.Add(name, value)
}
}
}
url.RawQuery = q.Encode()
return url.String(), nil
}

View file

@ -94,8 +94,8 @@ package mailgun
import ( import (
"fmt" "fmt"
"github.com/mbanzon/simplehttp"
"io" "io"
"net/http"
"time" "time"
) )
@ -131,6 +131,8 @@ type Mailgun interface {
Domain() string Domain() string
ApiKey() string ApiKey() string
PublicApiKey() string PublicApiKey() string
Client() *http.Client
SetClient(client *http.Client)
Send(m *Message) (string, string, error) Send(m *Message) (string, string, error)
ValidateEmail(email string) (EmailVerification, error) ValidateEmail(email string) (EmailVerification, error)
ParseAddresses(addresses ...string) ([]string, []string, error) ParseAddresses(addresses ...string) ([]string, []string, error)
@ -195,11 +197,17 @@ type MailgunImpl struct {
domain string domain string
apiKey string apiKey string
publicApiKey string publicApiKey string
client *http.Client
} }
// NewMailGun creates a new client instance. // NewMailGun creates a new client instance.
func NewMailgun(domain, apiKey, publicApiKey string) Mailgun { func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
m := MailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey} m := MailgunImpl{
domain: domain,
apiKey: apiKey,
publicApiKey: publicApiKey,
client: http.DefaultClient,
}
return &m return &m
} }
@ -218,6 +226,16 @@ func (m *MailgunImpl) PublicApiKey() string {
return m.publicApiKey return m.publicApiKey
} }
// Client returns the HTTP client configured for this client.
func (m *MailgunImpl) Client() *http.Client {
return m.client
}
// SetClient updates the HTTP client for this client.
func (m *MailgunImpl) SetClient(c *http.Client) {
m.client = c
}
// generateApiUrl renders a URL for an API endpoint using the domain and endpoint name. // generateApiUrl renders a URL for an API endpoint using the domain and endpoint name.
func generateApiUrl(m Mailgun, endpoint string) string { func generateApiUrl(m Mailgun, endpoint string) string {
return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint) return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint)
@ -270,8 +288,8 @@ func generatePublicApiUrl(endpoint string) string {
} }
// generateParameterizedUrl works as generateApiUrl, but supports query parameters. // generateParameterizedUrl works as generateApiUrl, but supports query parameters.
func generateParameterizedUrl(m Mailgun, endpoint string, payload simplehttp.Payload) (string, error) { func generateParameterizedUrl(m Mailgun, endpoint string, payload payload) (string, error) {
paramBuffer, err := payload.GetPayloadBuffer() paramBuffer, err := payload.getPayloadBuffer()
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -1,6 +1,7 @@
package mailgun package mailgun
import ( import (
"net/http"
"strconv" "strconv"
"testing" "testing"
) )
@ -23,6 +24,15 @@ func TestMailgun(t *testing.T) {
if publicApiKey != m.PublicApiKey() { if publicApiKey != m.PublicApiKey() {
t.Fatal("PublicApiKey not equal!") t.Fatal("PublicApiKey not equal!")
} }
if http.DefaultClient != m.Client() {
t.Fatal("HTTP client not default!")
}
client := new(http.Client)
m.SetClient(client)
if client != m.Client() {
t.Fatal("HTTP client not equal!")
}
} }
func TestBounceGetCode(t *testing.T) { func TestBounceGetCode(t *testing.T) {

View file

@ -3,7 +3,6 @@ package mailgun
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
) )
@ -63,17 +62,18 @@ type Member struct {
// GetLists returns the specified set of mailing lists administered by your account. // GetLists returns the specified set of mailing lists administered by your account.
func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, error) { func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint)) r := newHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if limit != DefaultLimit { if limit != DefaultLimit {
p.AddValue("limit", strconv.Itoa(limit)) p.addValue("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
p.AddValue("skip", strconv.Itoa(skip)) p.addValue("skip", strconv.Itoa(skip))
} }
if filter != "" { if filter != "" {
p.AddValue("address", filter) p.addValue("address", filter)
} }
var envelope struct { var envelope struct {
Items []List `json:"items"` Items []List `json:"items"`
@ -83,7 +83,7 @@ func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, er
if err != nil { if err != nil {
return -1, nil, err return -1, nil, err
} }
err = response.ParseFromJSON(&envelope) err = response.parseFromJSON(&envelope)
return envelope.TotalCount, envelope.Items, err return envelope.TotalCount, envelope.Items, err
} }
@ -93,35 +93,37 @@ func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, er
// If unspecified, Description remains blank, // If unspecified, Description remains blank,
// while AccessLevel defaults to Everyone. // while AccessLevel defaults to Everyone.
func (mg *MailgunImpl) CreateList(prototype List) (List, error) { func (mg *MailgunImpl) CreateList(prototype List) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint)) r := newHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if prototype.Address != "" { if prototype.Address != "" {
p.AddValue("address", prototype.Address) p.addValue("address", prototype.Address)
} }
if prototype.Name != "" { if prototype.Name != "" {
p.AddValue("name", prototype.Name) p.addValue("name", prototype.Name)
} }
if prototype.Description != "" { if prototype.Description != "" {
p.AddValue("description", prototype.Description) p.addValue("description", prototype.Description)
} }
if prototype.AccessLevel != "" { if prototype.AccessLevel != "" {
p.AddValue("access_level", prototype.AccessLevel) p.addValue("access_level", prototype.AccessLevel)
} }
response, err := makePostRequest(r, p) response, err := makePostRequest(r, p)
if err != nil { if err != nil {
return List{}, err return List{}, err
} }
var l List var l List
err = response.ParseFromJSON(&l) err = response.parseFromJSON(&l)
return l, err return l, err
} }
// DeleteList removes all current members of the list, then removes the list itself. // DeleteList removes all current members of the list, then removes the list itself.
// Attempts to send e-mail to the list will fail subsequent to this call. // Attempts to send e-mail to the list will fail subsequent to this call.
func (mg *MailgunImpl) DeleteList(addr string) error { func (mg *MailgunImpl) DeleteList(addr string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr) r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }
@ -129,13 +131,14 @@ func (mg *MailgunImpl) DeleteList(addr string) error {
// GetListByAddress allows your application to recover the complete List structure // GetListByAddress allows your application to recover the complete List structure
// representing a mailing list, so long as you have its e-mail address. // representing a mailing list, so long as you have its e-mail address.
func (mg *MailgunImpl) GetListByAddress(addr string) (List, error) { func (mg *MailgunImpl) GetListByAddress(addr string) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr) r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
response, err := makeGetRequest(r) response, err := makeGetRequest(r)
var envelope struct { var envelope struct {
List `json:"list"` List `json:"list"`
} }
err = response.ParseFromJSON(&envelope) err = response.parseFromJSON(&envelope)
return envelope.List, err return envelope.List, err
} }
@ -147,27 +150,28 @@ func (mg *MailgunImpl) GetListByAddress(addr string) (List, error) {
// e-mail sent to the old address will not succeed. // e-mail sent to the old address will not succeed.
// Make sure you account for the change accordingly. // Make sure you account for the change accordingly.
func (mg *MailgunImpl) UpdateList(addr string, prototype List) (List, error) { func (mg *MailgunImpl) UpdateList(addr string, prototype List) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr) r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if prototype.Address != "" { if prototype.Address != "" {
p.AddValue("address", prototype.Address) p.addValue("address", prototype.Address)
} }
if prototype.Name != "" { if prototype.Name != "" {
p.AddValue("name", prototype.Name) p.addValue("name", prototype.Name)
} }
if prototype.Description != "" { if prototype.Description != "" {
p.AddValue("description", prototype.Description) p.addValue("description", prototype.Description)
} }
if prototype.AccessLevel != "" { if prototype.AccessLevel != "" {
p.AddValue("access_level", prototype.AccessLevel) p.addValue("access_level", prototype.AccessLevel)
} }
var l List var l List
response, err := makePutRequest(r, p) response, err := makePutRequest(r, p)
if err != nil { if err != nil {
return l, err return l, err
} }
err = response.ParseFromJSON(&l) err = response.parseFromJSON(&l)
return l, err return l, err
} }
@ -176,17 +180,18 @@ func (mg *MailgunImpl) UpdateList(addr string, prototype List) (List, error) {
// All indicates that you want both Members and unsubscribed members alike, while // All indicates that you want both Members and unsubscribed members alike, while
// Subscribed and Unsubscribed indicate you want only those eponymous subsets. // Subscribed and Unsubscribed indicate you want only those eponymous subsets.
func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, []Member, error) { func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, []Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr)) r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if limit != DefaultLimit { if limit != DefaultLimit {
p.AddValue("limit", strconv.Itoa(limit)) p.addValue("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
p.AddValue("skip", strconv.Itoa(skip)) p.addValue("skip", strconv.Itoa(skip))
} }
if s != nil { if s != nil {
p.AddValue("subscribed", yesNo(*s)) p.addValue("subscribed", yesNo(*s))
} }
var envelope struct { var envelope struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
@ -196,15 +201,16 @@ func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, [
if err != nil { if err != nil {
return -1, nil, err return -1, nil, err
} }
err = response.ParseFromJSON(&envelope) err = response.parseFromJSON(&envelope)
return envelope.TotalCount, envelope.Items, err return envelope.TotalCount, envelope.Items, err
} }
// GetMemberByAddress returns a complete Member structure for a member of a mailing list, // GetMemberByAddress returns a complete Member structure for a member of a mailing list,
// given only their subscription e-mail address. // given only their subscription e-mail address.
func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) { func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s) r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
response, err := makeGetRequest(r) response, err := makeGetRequest(r)
if err != nil { if err != nil {
return Member{}, err return Member{}, err
@ -212,7 +218,7 @@ func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) {
var envelope struct { var envelope struct {
Member Member `json:"member"` Member Member `json:"member"`
} }
err = response.ParseFromJSON(&envelope) err = response.parseFromJSON(&envelope)
return envelope.Member, err return envelope.Member, err
} }
@ -225,15 +231,16 @@ func (mg *MailgunImpl) CreateMember(merge bool, addr string, prototype Member) e
return err return err
} }
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr)) r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewFormDataPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("upsert", yesNo(merge)) p := newFormDataPayload()
p.AddValue("address", prototype.Address) p.addValue("upsert", yesNo(merge))
p.AddValue("name", prototype.Name) p.addValue("address", prototype.Address)
p.AddValue("vars", string(vs)) p.addValue("name", prototype.Name)
p.addValue("vars", string(vs))
if prototype.Subscribed != nil { if prototype.Subscribed != nil {
p.AddValue("subscribed", yesNo(*prototype.Subscribed)) p.addValue("subscribed", yesNo(*prototype.Subscribed))
} }
_, err = makePostRequest(r, p) _, err = makePostRequest(r, p)
return err return err
@ -242,24 +249,25 @@ func (mg *MailgunImpl) CreateMember(merge bool, addr string, prototype Member) e
// UpdateMember lets you change certain details about the indicated mailing list member. // UpdateMember lets you change certain details about the indicated mailing list member.
// Address, Name, Vars, and Subscribed fields may be changed. // Address, Name, Vars, and Subscribed fields may be changed.
func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, error) { func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s) r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewFormDataPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newFormDataPayload()
if prototype.Address != "" { if prototype.Address != "" {
p.AddValue("address", prototype.Address) p.addValue("address", prototype.Address)
} }
if prototype.Name != "" { if prototype.Name != "" {
p.AddValue("name", prototype.Name) p.addValue("name", prototype.Name)
} }
if prototype.Vars != nil { if prototype.Vars != nil {
vs, err := json.Marshal(prototype.Vars) vs, err := json.Marshal(prototype.Vars)
if err != nil { if err != nil {
return Member{}, err return Member{}, err
} }
p.AddValue("vars", string(vs)) p.addValue("vars", string(vs))
} }
if prototype.Subscribed != nil { if prototype.Subscribed != nil {
p.AddValue("subscribed", yesNo(*prototype.Subscribed)) p.addValue("subscribed", yesNo(*prototype.Subscribed))
} }
response, err := makePutRequest(r, p) response, err := makePutRequest(r, p)
if err != nil { if err != nil {
@ -268,14 +276,15 @@ func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, erro
var envelope struct { var envelope struct {
Member Member `json:"member"` Member Member `json:"member"`
} }
err = response.ParseFromJSON(&envelope) err = response.parseFromJSON(&envelope)
return envelope.Member, err return envelope.Member, err
} }
// DeleteMember removes the member from the list. // DeleteMember removes the member from the list.
func (mg *MailgunImpl) DeleteMember(member, addr string) error { func (mg *MailgunImpl) DeleteMember(member, addr string) error {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + "/" + member) r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + "/" + member)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }
@ -290,18 +299,19 @@ func (mg *MailgunImpl) DeleteMember(member, addr string) error {
// Otherwise, each Member needs to have at least the Address field filled out. // 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. // Other fields are optional, but may be set according to your needs.
func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error { func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json") r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewFormDataPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newFormDataPayload()
if s != nil { if s != nil {
p.AddValue("subscribed", yesNo(*s)) p.addValue("subscribed", yesNo(*s))
} }
bs, err := json.Marshal(newMembers) bs, err := json.Marshal(newMembers)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(string(bs)) fmt.Println(string(bs))
p.AddValue("members", string(bs)) p.addValue("members", string(bs))
_, err = makePostRequest(r, p) _, err = makePostRequest(r, p)
return err return err
} }

View file

@ -5,8 +5,6 @@ import (
"errors" "errors"
"io" "io"
"time" "time"
"github.com/mbanzon/simplehttp"
) )
// MaxNumberOfRecipients represents the largest batch of recipients that Mailgun can support in a single API call. // MaxNumberOfRecipients represents the largest batch of recipients that Mailgun can support in a single API call.
@ -118,7 +116,7 @@ type features interface {
addCC(string) addCC(string)
addBCC(string) addBCC(string)
setHtml(string) setHtml(string)
addValues(*simplehttp.FormDataPayload) addValues(*formDataPayload)
isValid() bool isValid() bool
endpoint() string endpoint() string
recipientCount() int recipientCount() int
@ -423,44 +421,44 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
if !isValid(message) { if !isValid(message) {
err = errors.New("Message not valid") err = errors.New("Message not valid")
} else { } else {
payload := simplehttp.NewFormDataPayload() payload := newFormDataPayload()
message.specific.addValues(payload) message.specific.addValues(payload)
for _, to := range message.to { for _, to := range message.to {
payload.AddValue("to", to) payload.addValue("to", to)
} }
for _, tag := range message.tags { for _, tag := range message.tags {
payload.AddValue("o:tag", tag) payload.addValue("o:tag", tag)
} }
for _, campaign := range message.campaigns { for _, campaign := range message.campaigns {
payload.AddValue("o:campaign", campaign) payload.addValue("o:campaign", campaign)
} }
if message.dkimSet { if message.dkimSet {
payload.AddValue("o:dkim", yesNo(message.dkim)) payload.addValue("o:dkim", yesNo(message.dkim))
} }
if message.deliveryTime != nil { if message.deliveryTime != nil {
payload.AddValue("o:deliverytime", formatMailgunTime(message.deliveryTime)) payload.addValue("o:deliverytime", formatMailgunTime(message.deliveryTime))
} }
if message.testMode { if message.testMode {
payload.AddValue("o:testmode", "yes") payload.addValue("o:testmode", "yes")
} }
if message.trackingSet { if message.trackingSet {
payload.AddValue("o:tracking", yesNo(message.tracking)) payload.addValue("o:tracking", yesNo(message.tracking))
} }
if message.trackingClicksSet { if message.trackingClicksSet {
payload.AddValue("o:tracking-clicks", yesNo(message.trackingClicks)) payload.addValue("o:tracking-clicks", yesNo(message.trackingClicks))
} }
if message.trackingOpensSet { if message.trackingOpensSet {
payload.AddValue("o:tracking-opens", yesNo(message.trackingOpens)) payload.addValue("o:tracking-opens", yesNo(message.trackingOpens))
} }
if message.headers != nil { if message.headers != nil {
for header, value := range message.headers { for header, value := range message.headers {
payload.AddValue("h:"+header, value) payload.addValue("h:"+header, value)
} }
} }
if message.variables != nil { if message.variables != nil {
for variable, value := range message.variables { for variable, value := range message.variables {
payload.AddValue("v:"+variable, value) payload.addValue("v:"+variable, value)
} }
} }
if message.recipientVariables != nil { if message.recipientVariables != nil {
@ -468,26 +466,27 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
payload.AddValue("recipient-variables", string(j)) payload.addValue("recipient-variables", string(j))
} }
if message.attachments != nil { if message.attachments != nil {
for _, attachment := range message.attachments { for _, attachment := range message.attachments {
payload.AddFile("attachment", attachment) payload.addFile("attachment", attachment)
} }
} }
if message.readerAttachments != nil { if message.readerAttachments != nil {
for _, readerAttachment := range message.readerAttachments { for _, readerAttachment := range message.readerAttachments {
payload.AddReadCloser("attachment", readerAttachment.Filename, readerAttachment.ReadCloser) payload.addReadCloser("attachment", readerAttachment.Filename, readerAttachment.ReadCloser)
} }
} }
if message.inlines != nil { if message.inlines != nil {
for _, inline := range message.inlines { for _, inline := range message.inlines {
payload.AddFile("inline", inline) payload.addFile("inline", inline)
} }
} }
r := simplehttp.NewHTTPRequest(generateApiUrl(m, message.specific.endpoint())) r := newHTTPRequest(generateApiUrl(m, message.specific.endpoint()))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var response sendMessageResponse var response sendMessageResponse
err = postResponseFromJSON(r, payload, &response) err = postResponseFromJSON(r, payload, &response)
@ -500,23 +499,23 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
return return
} }
func (pm *plainMessage) addValues(p *simplehttp.FormDataPayload) { func (pm *plainMessage) addValues(p *formDataPayload) {
p.AddValue("from", pm.from) p.addValue("from", pm.from)
p.AddValue("subject", pm.subject) p.addValue("subject", pm.subject)
p.AddValue("text", pm.text) p.addValue("text", pm.text)
for _, cc := range pm.cc { for _, cc := range pm.cc {
p.AddValue("cc", cc) p.addValue("cc", cc)
} }
for _, bcc := range pm.bcc { for _, bcc := range pm.bcc {
p.AddValue("bcc", bcc) p.addValue("bcc", bcc)
} }
if pm.html != "" { if pm.html != "" {
p.AddValue("html", pm.html) p.addValue("html", pm.html)
} }
} }
func (mm *mimeMessage) addValues(p *simplehttp.FormDataPayload) { func (mm *mimeMessage) addValues(p *formDataPayload) {
p.AddReadCloser("message", "message.mime", mm.body) p.addReadCloser("message", "message.mime", mm.body)
} }
func (pm *plainMessage) endpoint() string { func (pm *plainMessage) endpoint() string {
@ -612,8 +611,9 @@ func validateStringList(list []string, requireOne bool) bool {
// This provides visibility into, e.g., replies to a message sent to a mailing list. // This provides visibility into, e.g., replies to a message sent to a mailing list.
func (mg *MailgunImpl) GetStoredMessage(id string) (StoredMessage, error) { func (mg *MailgunImpl) GetStoredMessage(id string) (StoredMessage, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id) url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url) r := newHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var response StoredMessage var response StoredMessage
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
@ -625,9 +625,10 @@ func (mg *MailgunImpl) GetStoredMessage(id string) (StoredMessage, error) {
// thus delegates to the caller the required parsing. // thus delegates to the caller the required parsing.
func (mg *MailgunImpl) GetStoredMessageRaw(id string) (StoredMessageRaw, error) { func (mg *MailgunImpl) GetStoredMessageRaw(id string) (StoredMessageRaw, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id) url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url) r := newHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.AddHeader("Accept", "message/rfc2822") r.setBasicAuth(basicAuthUser, mg.ApiKey())
r.addHeader("Accept", "message/rfc2822")
var response StoredMessageRaw var response StoredMessageRaw
err := getResponseFromJSON(r, &response) err := getResponseFromJSON(r, &response)
@ -640,8 +641,9 @@ func (mg *MailgunImpl) GetStoredMessageRaw(id string) (StoredMessageRaw, error)
// Consult the current Mailgun API documentation for more details. // Consult the current Mailgun API documentation for more details.
func (mg *MailgunImpl) DeleteStoredMessage(id string) error { func (mg *MailgunImpl) DeleteStoredMessage(id string) error {
url := generateStoredMessageUrl(mg, messagesEndpoint, id) url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url) r := newHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -2,7 +2,6 @@ package mailgun
import ( import (
"fmt" "fmt"
"github.com/mbanzon/simplehttp"
) )
// The MailgunGoUserAgent identifies the client to the server, for logging purposes. // The MailgunGoUserAgent identifies the client to the server, for logging purposes.
@ -17,12 +16,13 @@ type UnexpectedResponseError struct {
Expected []int Expected []int
Actual int Actual int
URL string URL string
Data []byte
} }
// String() converts the error into a human-readable, logfmt-compliant string. // String() converts the error into a human-readable, logfmt-compliant string.
// See http://godoc.org/github.com/kr/logfmt for details on logfmt formatting. // See http://godoc.org/github.com/kr/logfmt for details on logfmt formatting.
func (e *UnexpectedResponseError) String() string { func (e *UnexpectedResponseError) String() string {
return fmt.Sprintf("UnexpectedResponseError URL=%s ExpectedOneOf=%#v Got=%d", e.URL, e.Expected, e.Actual) return fmt.Sprintf("UnexpectedResponseError URL=%s ExpectedOneOf=%#v Got=%d Error: %s", e.URL, e.Expected, e.Actual, string(e.Data))
} }
// Error() performs as String(). // Error() performs as String().
@ -31,11 +31,12 @@ func (e *UnexpectedResponseError) Error() string {
} }
// newError creates a new error condition to be returned. // newError creates a new error condition to be returned.
func newError(url string, expected []int, got int) error { func newError(url string, expected []int, got *httpResponse) error {
return &UnexpectedResponseError{ return &UnexpectedResponseError{
URL: url, URL: url,
Expected: expected, Expected: expected,
Actual: got, Actual: got.Code,
Data: got.Data,
} }
} }
@ -56,97 +57,97 @@ var expected = []int{200, 202, 204}
// makeRequest shim performs a generic request, checking for a positive outcome. // makeRequest shim performs a generic request, checking for a positive outcome.
// See simplehttp.MakeRequest for more details. // See simplehttp.MakeRequest for more details.
func makeRequest(r *simplehttp.HTTPRequest, kind string, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) { func makeRequest(r *httpRequest, kind string, p payload) (*httpResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeRequest(kind, p) rsp, err := r.makeRequest(kind, p)
if (err == nil) && notGood(rsp.Code, expected) { if (err == nil) && notGood(rsp.Code, expected) {
return rsp, newError(r.URL, expected, rsp.Code) return rsp, newError(r.URL, expected, rsp)
} }
return rsp, err return rsp, err
} }
// getResponseFromJSON shim performs a GET request, checking for a positive outcome. // getResponseFromJSON shim performs a GET request, checking for a positive outcome.
// See simplehttp.GetResponseFromJSON for more details. // See simplehttp.GetResponseFromJSON for more details.
func getResponseFromJSON(r *simplehttp.HTTPRequest, v interface{}) error { func getResponseFromJSON(r *httpRequest, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakeGetRequest() response, err := r.makeGetRequest()
if err != nil { if err != nil {
return err return err
} }
if notGood(response.Code, expected) { if notGood(response.Code, expected) {
return newError(r.URL, expected, response.Code) return newError(r.URL, expected, response)
} }
return response.ParseFromJSON(v) return response.parseFromJSON(v)
} }
// postResponseFromJSON shim performs a POST request, checking for a positive outcome. // postResponseFromJSON shim performs a POST request, checking for a positive outcome.
// See simplehttp.PostResponseFromJSON for more details. // See simplehttp.PostResponseFromJSON for more details.
func postResponseFromJSON(r *simplehttp.HTTPRequest, p simplehttp.Payload, v interface{}) error { func postResponseFromJSON(r *httpRequest, p payload, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakePostRequest(p) response, err := r.makePostRequest(p)
if err != nil { if err != nil {
return err return err
} }
if notGood(response.Code, expected) { if notGood(response.Code, expected) {
return newError(r.URL, expected, response.Code) return newError(r.URL, expected, response)
} }
return response.ParseFromJSON(v) return response.parseFromJSON(v)
} }
// putResponseFromJSON shim performs a PUT request, checking for a positive outcome. // putResponseFromJSON shim performs a PUT request, checking for a positive outcome.
// See simplehttp.PutResponseFromJSON for more details. // See simplehttp.PutResponseFromJSON for more details.
func putResponseFromJSON(r *simplehttp.HTTPRequest, p simplehttp.Payload, v interface{}) error { func putResponseFromJSON(r *httpRequest, p payload, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakePutRequest(p) response, err := r.makePutRequest(p)
if err != nil { if err != nil {
return err return err
} }
if notGood(response.Code, expected) { if notGood(response.Code, expected) {
return newError(r.URL, expected, response.Code) return newError(r.URL, expected, response)
} }
return response.ParseFromJSON(v) return response.parseFromJSON(v)
} }
// makeGetRequest shim performs a GET request, checking for a positive outcome. // makeGetRequest shim performs a GET request, checking for a positive outcome.
// See simplehttp.MakeGetRequest for more details. // See simplehttp.MakeGetRequest for more details.
func makeGetRequest(r *simplehttp.HTTPRequest) (*simplehttp.HTTPResponse, error) { func makeGetRequest(r *httpRequest) (*httpResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeGetRequest() rsp, err := r.makeGetRequest()
if (err == nil) && notGood(rsp.Code, expected) { if (err == nil) && notGood(rsp.Code, expected) {
return rsp, newError(r.URL, expected, rsp.Code) return rsp, newError(r.URL, expected, rsp)
} }
return rsp, err return rsp, err
} }
// makePostRequest shim performs a POST request, checking for a positive outcome. // makePostRequest shim performs a POST request, checking for a positive outcome.
// See simplehttp.MakePostRequest for more details. // See simplehttp.MakePostRequest for more details.
func makePostRequest(r *simplehttp.HTTPRequest, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) { func makePostRequest(r *httpRequest, p payload) (*httpResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakePostRequest(p) rsp, err := r.makePostRequest(p)
if (err == nil) && notGood(rsp.Code, expected) { if (err == nil) && notGood(rsp.Code, expected) {
return rsp, newError(r.URL, expected, rsp.Code) return rsp, newError(r.URL, expected, rsp)
} }
return rsp, err return rsp, err
} }
// makePutRequest shim performs a PUT request, checking for a positive outcome. // makePutRequest shim performs a PUT request, checking for a positive outcome.
// See simplehttp.MakePutRequest for more details. // See simplehttp.MakePutRequest for more details.
func makePutRequest(r *simplehttp.HTTPRequest, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) { func makePutRequest(r *httpRequest, p payload) (*httpResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakePutRequest(p) rsp, err := r.makePutRequest(p)
if (err == nil) && notGood(rsp.Code, expected) { if (err == nil) && notGood(rsp.Code, expected) {
return rsp, newError(r.URL, expected, rsp.Code) return rsp, newError(r.URL, expected, rsp)
} }
return rsp, err return rsp, err
} }
// makeDeleteRequest shim performs a DELETE request, checking for a positive outcome. // makeDeleteRequest shim performs a DELETE request, checking for a positive outcome.
// See simplehttp.MakeDeleteRequest for more details. // See simplehttp.MakeDeleteRequest for more details.
func makeDeleteRequest(r *simplehttp.HTTPRequest) (*simplehttp.HTTPResponse, error) { func makeDeleteRequest(r *httpRequest) (*httpResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent) r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeDeleteRequest() rsp, err := r.makeDeleteRequest()
if (err == nil) && notGood(rsp.Code, expected) { if (err == nil) && notGood(rsp.Code, expected) {
return rsp, newError(r.URL, expected, rsp.Code) return rsp, newError(r.URL, expected, rsp)
} }
return rsp, err return rsp, err
} }

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
) )
@ -34,14 +33,15 @@ type Route struct {
// messages sent to a specfic address on your domain. // messages sent to a specfic address on your domain.
// See the Mailgun documentation for more information. // See the Mailgun documentation for more information.
func (mg *MailgunImpl) GetRoutes(limit, skip int) (int, []Route, error) { func (mg *MailgunImpl) GetRoutes(limit, skip int) (int, []Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint)) r := newHTTPRequest(generatePublicApiUrl(routesEndpoint))
if limit != DefaultLimit { if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
@ -59,14 +59,15 @@ func (mg *MailgunImpl) GetRoutes(limit, skip int) (int, []Route, error) {
// only a subset of the fields influence the operation. // only a subset of the fields influence the operation.
// See the Route structure definition for more details. // See the Route structure definition for more details.
func (mg *MailgunImpl) CreateRoute(prototype Route) (Route, error) { func (mg *MailgunImpl) CreateRoute(prototype Route) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint)) r := newHTTPRequest(generatePublicApiUrl(routesEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("priority", strconv.Itoa(prototype.Priority)) p := newUrlEncodedPayload()
p.AddValue("description", prototype.Description) p.addValue("priority", strconv.Itoa(prototype.Priority))
p.AddValue("expression", prototype.Expression) p.addValue("description", prototype.Description)
p.addValue("expression", prototype.Expression)
for _, action := range prototype.Actions { for _, action := range prototype.Actions {
p.AddValue("action", action) p.addValue("action", action)
} }
var envelope struct { var envelope struct {
Message string `json:"message"` Message string `json:"message"`
@ -80,16 +81,18 @@ func (mg *MailgunImpl) CreateRoute(prototype Route) (Route, error) {
// To avoid ambiguity, Mailgun identifies the route by unique ID. // To avoid ambiguity, Mailgun identifies the route by unique ID.
// See the Route structure definition and the Mailgun API documentation for more details. // See the Route structure definition and the Mailgun API documentation for more details.
func (mg *MailgunImpl) DeleteRoute(id string) error { func (mg *MailgunImpl) DeleteRoute(id string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id) r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }
// GetRouteByID retrieves the complete route definition associated with the unique route ID. // GetRouteByID retrieves the complete route definition associated with the unique route ID.
func (mg *MailgunImpl) GetRouteByID(id string) (Route, error) { func (mg *MailgunImpl) GetRouteByID(id string) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id) r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
Message string `json:"message"` Message string `json:"message"`
*Route `json:"route"` *Route `json:"route"`
@ -102,21 +105,22 @@ func (mg *MailgunImpl) GetRouteByID(id string) (Route, error) {
// Only those route fields which are non-zero or non-empty are updated. // Only those route fields which are non-zero or non-empty are updated.
// All other fields remain as-is. // All other fields remain as-is.
func (mg *MailgunImpl) UpdateRoute(id string, route Route) (Route, error) { func (mg *MailgunImpl) UpdateRoute(id string, route Route) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id) r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if route.Priority != 0 { if route.Priority != 0 {
p.AddValue("priority", strconv.Itoa(route.Priority)) p.addValue("priority", strconv.Itoa(route.Priority))
} }
if route.Description != "" { if route.Description != "" {
p.AddValue("description", route.Description) p.addValue("description", route.Description)
} }
if route.Expression != "" { if route.Expression != "" {
p.AddValue("expression", route.Expression) p.addValue("expression", route.Expression)
} }
if route.Actions != nil { if route.Actions != nil {
for _, action := range route.Actions { for _, action := range route.Actions {
p.AddValue("action", action) p.addValue("action", action)
} }
} }
// For some reason, this API function just returns a bare Route on success. // For some reason, this API function just returns a bare Route on success.

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
) )
@ -28,14 +27,15 @@ type complaintsEnvelope struct {
// Recipients of your messages can click on a link which sends feedback to Mailgun // Recipients of your messages can click on a link which sends feedback to Mailgun
// indicating that the message they received is, to them, spam. // indicating that the message they received is, to them, spam.
func (m *MailgunImpl) GetComplaints(limit, skip int) (int, []Complaint, error) { func (m *MailgunImpl) GetComplaints(limit, skip int) (int, []Complaint, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint)) r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
if limit != -1 { if limit != -1 {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != -1 { if skip != -1 {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
var envelope complaintsEnvelope var envelope complaintsEnvelope
@ -49,8 +49,9 @@ func (m *MailgunImpl) GetComplaints(limit, skip int) (int, []Complaint, error) {
// GetSingleComplaint returns a single complaint record filed by a recipient at the email address provided. // GetSingleComplaint returns a single complaint record filed by a recipient at the email address provided.
// If no complaint exists, the Complaint instance returned will be empty. // If no complaint exists, the Complaint instance returned will be empty.
func (m *MailgunImpl) GetSingleComplaint(address string) (Complaint, error) { func (m *MailgunImpl) GetSingleComplaint(address string) (Complaint, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address) r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var c Complaint var c Complaint
err := getResponseFromJSON(r, &c) err := getResponseFromJSON(r, &c)
@ -60,10 +61,11 @@ func (m *MailgunImpl) GetSingleComplaint(address string) (Complaint, error) {
// CreateComplaint registers the specified address as a recipient who has complained of receiving spam // CreateComplaint registers the specified address as a recipient who has complained of receiving spam
// from your domain. // from your domain.
func (m *MailgunImpl) CreateComplaint(address string) error { func (m *MailgunImpl) CreateComplaint(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint)) r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, m.ApiKey())
p.AddValue("address", address) p := newUrlEncodedPayload()
p.addValue("address", address)
_, err := makePostRequest(r, p) _, err := makePostRequest(r, p)
return err return err
} }
@ -71,8 +73,9 @@ func (m *MailgunImpl) CreateComplaint(address string) error {
// DeleteComplaint removes a previously registered e-mail address from the list of people who complained // DeleteComplaint removes a previously registered e-mail address from the list of people who complained
// of receiving spam from your domain. // of receiving spam from your domain.
func (m *MailgunImpl) DeleteComplaint(address string) error { func (m *MailgunImpl) DeleteComplaint(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address) r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
"time" "time"
) )
@ -23,23 +22,24 @@ type statsEnvelope struct {
// Events start at the given start date, if one is provided. // Events start at the given start date, if one is provided.
// If not, this function will consider all stated events dating to the creation of the sending domain. // If not, this function will consider all stated events dating to the creation of the sending domain.
func (m *MailgunImpl) GetStats(limit int, skip int, startDate *time.Time, event ...string) (int, []Stat, error) { func (m *MailgunImpl) GetStats(limit int, skip int, startDate *time.Time, event ...string) (int, []Stat, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, statsEndpoint)) r := newHTTPRequest(generateApiUrl(m, statsEndpoint))
if limit != -1 { if limit != -1 {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != -1 { if skip != -1 {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
if startDate != nil { if startDate != nil {
r.AddParameter("start-date", startDate.Format(time.RFC3339)) r.addParameter("start-date", startDate.Format(time.RFC3339))
} }
for _, e := range event { for _, e := range event {
r.AddParameter("event", e) r.addParameter("event", e)
} }
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var res statsEnvelope var res statsEnvelope
err := getResponseFromJSON(r, &res) err := getResponseFromJSON(r, &res)
@ -52,8 +52,9 @@ func (m *MailgunImpl) GetStats(limit int, skip int, startDate *time.Time, event
// DeleteTag removes all counters for a particular tag, including the tag itself. // DeleteTag removes all counters for a particular tag, including the tag itself.
func (m *MailgunImpl) DeleteTag(tag string) error { func (m *MailgunImpl) DeleteTag(tag string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, deleteTagEndpoint) + "/" + tag) r := newHTTPRequest(generateApiUrl(m, deleteTagEndpoint) + "/" + tag)
r.SetBasicAuth(basicAuthUser, m.ApiKey()) r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,7 +1,6 @@
package mailgun package mailgun
import ( import (
"github.com/mbanzon/simplehttp"
"strconv" "strconv"
) )
@ -15,14 +14,15 @@ type Unsubscription struct {
// GetUnsubscribes retrieves a list of unsubscriptions issued by recipients of mail from your domain. // GetUnsubscribes retrieves a list of unsubscriptions issued by recipients of mail from your domain.
// Zero is a valid list length. // Zero is a valid list length.
func (mg *MailgunImpl) GetUnsubscribes(limit, skip int) (int, []Unsubscription, error) { func (mg *MailgunImpl) GetUnsubscribes(limit, skip int) (int, []Unsubscription, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint)) r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
if limit != DefaultLimit { if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit)) r.addParameter("limit", strconv.Itoa(limit))
} }
if skip != DefaultSkip { if skip != DefaultSkip {
r.AddParameter("skip", strconv.Itoa(skip)) r.addParameter("skip", strconv.Itoa(skip))
} }
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
Items []Unsubscription `json:"items"` Items []Unsubscription `json:"items"`
@ -34,8 +34,9 @@ func (mg *MailgunImpl) GetUnsubscribes(limit, skip int) (int, []Unsubscription,
// GetUnsubscribesByAddress retrieves a list of unsubscriptions by recipient address. // GetUnsubscribesByAddress retrieves a list of unsubscriptions by recipient address.
// Zero is a valid list length. // Zero is a valid list length.
func (mg *MailgunImpl) GetUnsubscribesByAddress(a string) (int, []Unsubscription, error) { func (mg *MailgunImpl) GetUnsubscribesByAddress(a string) (int, []Unsubscription, error) {
r := simplehttp.NewHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a)) r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
Items []Unsubscription `json:"items"` Items []Unsubscription `json:"items"`
@ -46,11 +47,12 @@ func (mg *MailgunImpl) GetUnsubscribesByAddress(a string) (int, []Unsubscription
// Unsubscribe adds an e-mail address to the domain's unsubscription table. // Unsubscribe adds an e-mail address to the domain's unsubscription table.
func (mg *MailgunImpl) Unsubscribe(a, t string) error { func (mg *MailgunImpl) Unsubscribe(a, t string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint)) r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("address", a) p := newUrlEncodedPayload()
p.AddValue("tag", t) p.addValue("address", a)
p.addValue("tag", t)
_, err := makePostRequest(r, p) _, err := makePostRequest(r, p)
return err return err
} }
@ -59,8 +61,9 @@ func (mg *MailgunImpl) Unsubscribe(a, t string) error {
// If passing in an ID (discoverable from, e.g., GetUnsubscribes()), the e-mail address associated // If passing in an ID (discoverable from, e.g., GetUnsubscribes()), the e-mail address associated
// with the given ID will be removed. // with the given ID will be removed.
func (mg *MailgunImpl) RemoveUnsubscribe(a string) error { func (mg *MailgunImpl) RemoveUnsubscribe(a string) error {
r := simplehttp.NewHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a)) r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }

View file

@ -1,14 +1,11 @@
package mailgun package mailgun
import (
"github.com/mbanzon/simplehttp"
)
// GetWebhooks returns the complete set of webhooks configured for your domain. // GetWebhooks returns the complete set of webhooks configured for your domain.
// Note that a zero-length mapping is not an error. // Note that a zero-length mapping is not an error.
func (mg *MailgunImpl) GetWebhooks() (map[string]string, error) { func (mg *MailgunImpl) GetWebhooks() (map[string]string, error) {
r := simplehttp.NewHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint)) r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
Webhooks map[string]interface{} `json:"webhooks"` Webhooks map[string]interface{} `json:"webhooks"`
} }
@ -27,42 +24,46 @@ func (mg *MailgunImpl) GetWebhooks() (map[string]string, error) {
// CreateWebhook installs a new webhook for your domain. // CreateWebhook installs a new webhook for your domain.
func (mg *MailgunImpl) CreateWebhook(t, u string) error { func (mg *MailgunImpl) CreateWebhook(t, u string) error {
r := simplehttp.NewHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint)) r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("id", t) p := newUrlEncodedPayload()
p.AddValue("url", u) p.addValue("id", t)
p.addValue("url", u)
_, err := makePostRequest(r, p) _, err := makePostRequest(r, p)
return err return err
} }
// DeleteWebhook removes the specified webhook from your domain's configuration. // DeleteWebhook removes the specified webhook from your domain's configuration.
func (mg *MailgunImpl) DeleteWebhook(t string) error { func (mg *MailgunImpl) DeleteWebhook(t string) error {
r := simplehttp.NewHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t) r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r) _, err := makeDeleteRequest(r)
return err return err
} }
// GetWebhookByType retrieves the currently assigned webhook URL associated with the provided type of webhook. // GetWebhookByType retrieves the currently assigned webhook URL associated with the provided type of webhook.
func (mg *MailgunImpl) GetWebhookByType(t string) (string, error) { func (mg *MailgunImpl) GetWebhookByType(t string) (string, error) {
r := simplehttp.NewHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t) r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct { var envelope struct {
Webhook struct { Webhook struct {
Url *string `json:"url"` Url string `json:"url"`
} `json:"webhook"` } `json:"webhook"`
} }
err := getResponseFromJSON(r, &envelope) err := getResponseFromJSON(r, &envelope)
return *envelope.Webhook.Url, err return envelope.Webhook.Url, err
} }
// UpdateWebhook replaces one webhook setting for another. // UpdateWebhook replaces one webhook setting for another.
func (mg *MailgunImpl) UpdateWebhook(t, u string) error { func (mg *MailgunImpl) UpdateWebhook(t, u string) error {
r := simplehttp.NewHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t) r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + t)
r.SetBasicAuth(basicAuthUser, mg.ApiKey()) r.setClient(mg.Client())
p := simplehttp.NewUrlEncodedPayload() r.setBasicAuth(basicAuthUser, mg.ApiKey())
p.AddValue("url", u) p := newUrlEncodedPayload()
p.addValue("url", u)
_, err := makePutRequest(r, p) _, err := makePutRequest(r, p)
return err return err
} }

View file

@ -1 +0,0 @@
desktop.ini

View file

@ -1,11 +0,0 @@
language: go
go:
- 1.3
- 1.4
env:
- GOARCH=amd64
- GOARCH=386
script:
- go get github.com/mbanzon/callbackenv
- go get github.com/mbanzon/dummyserver
- go test

View file

@ -1,27 +0,0 @@
Copyright (c) 2013-2014, Michael Banzon
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,15 +0,0 @@
simplehttp
==========
[![Build Status](https://travis-ci.org/mbanzon/simplehttp.png?branch=master)](https://travis-ci.org/mbanzon/simplehttp)
Simple HTTP library for Go.
This small library adds some utility functions for doing HTTP request and easilly gettings results as
structs from JSON and XML.
Supports alternative `http.Client` instances to support use on Google App Engine.
Examples are coming soon.
The code is released under a 3-clause BSD license. See the LICENSE file for more information.

View file

@ -1,17 +0,0 @@
package simplehttp
func (r *HTTPRequest) GetResponseFromJSON(v interface{}) error {
response, err := r.MakeGetRequest()
if err != nil {
return err
}
return response.ParseFromJSON(v)
}
func (r *HTTPRequest) PostResponseFromJSON(payload Payload, v interface{}) error {
response, err := r.MakePostRequest(payload)
if err != nil {
return err
}
return response.ParseFromJSON(v)
}

View file

@ -1,133 +0,0 @@
package simplehttp
import (
"encoding/json"
"encoding/xml"
"testing"
)
func TestParsingGetFromJson(t *testing.T) {
tmp := testStruct{
Value1: "1",
Value2: "2",
Value3: "3",
}
data, err := json.Marshal(tmp)
if err != nil {
t.Fail()
}
server.SetNextResponse(data)
request := NewHTTPRequest(dummyurl)
var retVal testStruct
err = request.GetResponseFromJSON(&retVal)
if err != nil {
t.Fail()
}
if tmp.Value1 != retVal.Value1 {
t.Fail()
}
if tmp.Value2 != retVal.Value2 {
t.Fail()
}
if tmp.Value3 != retVal.Value3 {
t.Fail()
}
}
func TestFailingParsingGetFromJson(t *testing.T) {
request := NewHTTPRequest(invalidurl)
var retVal testStruct
err := request.GetResponseFromJSON(&retVal)
if err == nil {
t.Fail()
}
}
func TestParsingPostFromJson(t *testing.T) {
tmp := testStruct{
Value1: "1",
Value2: "2",
Value3: "3",
}
data, err := json.Marshal(tmp)
if err != nil {
t.Fail()
}
server.SetNextResponse(data)
request := NewHTTPRequest(dummyurl)
var retVal testStruct
err = request.PostResponseFromJSON(nil, &retVal)
if err != nil {
t.Fail()
}
if tmp.Value1 != retVal.Value1 {
t.Fail()
}
if tmp.Value2 != retVal.Value2 {
t.Fail()
}
if tmp.Value3 != retVal.Value3 {
t.Fail()
}
}
func TestFailingParsingPostFromJson(t *testing.T) {
request := NewHTTPRequest(invalidurl)
var retVal testStruct
err := request.PostResponseFromJSON(nil, &retVal)
if err == nil {
t.Fail()
}
}
func TestParsingGetFromXml(t *testing.T) {
tmp := testStruct{
Value1: "1",
Value2: "2",
Value3: "3",
}
data, err := xml.Marshal(tmp)
if err != nil {
t.Fail()
}
server.SetNextResponse(data)
request := NewHTTPRequest(dummyurl)
var retVal testStruct
response, err := request.MakeGetRequest()
response.ParseFromXML(&retVal)
if err != nil {
t.Fail()
}
if tmp.Value1 != retVal.Value1 {
t.Fail()
}
if tmp.Value2 != retVal.Value2 {
t.Fail()
}
if tmp.Value3 != retVal.Value3 {
t.Fail()
}
}

View file

@ -1,31 +0,0 @@
package simplehttp
import (
"encoding/json"
"net/http"
)
func GetJSONInput(r *http.Request, w http.ResponseWriter, v interface{}) (err error) {
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(v)
if err != nil {
http.Error(w, "Bad request.", http.StatusBadRequest)
return err
}
return nil
}
func OutputJSON(w http.ResponseWriter, v interface{}) (err error) {
var data []byte
data, err = json.Marshal(v)
if err != nil {
http.Error(w, "Internal error.", http.StatusInternalServerError)
return err
}
w.Header().Add("Content-Type", "application/json")
_, err = w.Write(data)
return nil
}

View file

@ -1,15 +0,0 @@
package simplehttp
import (
"encoding/json"
"encoding/xml"
)
// Parses the HTTPResponse as JSON to the given interface.
func (r *HTTPResponse) ParseFromJSON(v interface{}) error {
return json.Unmarshal(r.Data, v)
}
func (r *HTTPResponse) ParseFromXML(v interface{}) error {
return xml.Unmarshal(r.Data, v)
}

View file

@ -1,38 +0,0 @@
package simplehttp
import (
"bytes"
"github.com/mbanzon/callbackenv"
"io/ioutil"
"testing"
)
const (
FILE_ENV = "SIMPLEHTTP_TEST_FILE"
)
func TestFormDataPayloadPost(t *testing.T) {
payload := NewFormDataPayload()
payload.AddValue("key", "value")
buf := &bytes.Buffer{}
buf.Write([]byte("testing testing testing"))
rc := ioutil.NopCloser(buf)
payload.AddReadCloser("foo", "bar", rc)
callbackenv.RequireEnv(FILE_ENV,
func(file string) {
payload.AddFile("file", file)
}, nil)
request := NewHTTPRequest(dummyurl)
request.MakePostRequest(payload)
}
func TestUrlEncodedPayloadPost(t *testing.T) {
payload := NewUrlEncodedPayload()
payload.AddValue("key", "value")
request := NewHTTPRequest(dummyurl)
request.MakePostRequest(payload)
}

View file

@ -1,141 +0,0 @@
package simplehttp
import (
"bytes"
"io"
"mime/multipart"
"net/url"
"os"
"path"
)
type keyValuePair struct {
key string
value string
}
type keyNameRC struct {
key string
name string
value io.ReadCloser
}
type Payload interface {
GetPayloadBuffer() (*bytes.Buffer, error)
GetContentType() string
}
type RawPayload struct {
Data []byte
}
type FormDataPayload struct {
contentType string
Values []keyValuePair
Files []keyValuePair
ReadClosers []keyNameRC
}
type UrlEncodedPayload struct {
Values []keyValuePair
}
func NewRawPayload(data []byte) *RawPayload {
return &RawPayload{Data: data}
}
func (r *RawPayload) GetPayloadBuffer() (*bytes.Buffer, error) {
data := &bytes.Buffer{}
c, err := data.Write(r.Data)
if c != len(r.Data) || err != nil {
return data, err
}
return data, nil
}
func (r *RawPayload) GetContentType() string {
return ""
}
func NewFormDataPayload() *FormDataPayload {
return &FormDataPayload{}
}
func (f *FormDataPayload) AddValue(key, value string) {
f.Values = append(f.Values, keyValuePair{key: key, value: value})
}
func (f *FormDataPayload) AddFile(key, file string) {
f.Files = append(f.Files, keyValuePair{key: key, value: file})
}
func (f *FormDataPayload) AddReadCloser(key, name string, rc io.ReadCloser) {
f.ReadClosers = append(f.ReadClosers, keyNameRC{key: key, name: name, value: rc})
}
func (f *FormDataPayload) GetPayloadBuffer() (*bytes.Buffer, error) {
data := &bytes.Buffer{}
writer := multipart.NewWriter(data)
defer writer.Close()
for _, keyVal := range f.Values {
if tmp, err := writer.CreateFormField(keyVal.key); err == nil {
tmp.Write([]byte(keyVal.value))
} else {
return nil, err
}
}
for _, file := range f.Files {
if tmp, err := writer.CreateFormFile(file.key, path.Base(file.value)); err == nil {
if fp, err := os.Open(file.value); err == nil {
defer fp.Close()
io.Copy(tmp, fp)
} else {
return nil, err
}
} else {
return nil, err
}
}
for _, file := range f.ReadClosers {
if tmp, err := writer.CreateFormFile(file.key, file.name); err == nil {
defer file.value.Close()
io.Copy(tmp, file.value)
} else {
return nil, err
}
}
f.contentType = writer.FormDataContentType()
return data, nil
}
func (f *FormDataPayload) GetContentType() string {
if f.contentType == "" {
f.GetPayloadBuffer()
}
return f.contentType
}
func NewUrlEncodedPayload() *UrlEncodedPayload {
return &UrlEncodedPayload{}
}
func (f *UrlEncodedPayload) AddValue(key, value string) {
f.Values = append(f.Values, keyValuePair{key: key, value: value})
}
func (f *UrlEncodedPayload) GetPayloadBuffer() (*bytes.Buffer, error) {
data := url.Values{}
for _, keyVal := range f.Values {
data.Add(keyVal.key, keyVal.value)
}
return bytes.NewBufferString(data.Encode()), nil
}
func (f *UrlEncodedPayload) GetContentType() string {
return "application/x-www-form-urlencoded"
}

View file

@ -1,74 +0,0 @@
package simplehttp
// Type to encapsulate basic authentication for requests.
type BasicAuthentication struct {
User string
Password string
}
// Type to wrap requests.
type Request struct {
Url string
Authentication BasicAuthentication
UserAgent string
Data []byte
}
func createHttpRequest(req Request) *HTTPRequest {
r := NewHTTPRequest(req.Url)
if req.Authentication.User != "" {
r.SetBasicAuth(req.Authentication.User, req.Authentication.Password)
}
if req.UserAgent != "" {
r.AddHeader("User-Agent", req.UserAgent)
}
return r
}
func (r Request) Get() (int, []byte, error) {
req := createHttpRequest(r)
res, err := req.MakeGetRequest()
if err == nil {
return res.Code, res.Data, err
} else {
return -1, nil, err
}
}
func (r Request) Post() (int, []byte, error) {
req := createHttpRequest(r)
var payload Payload = nil
if r.Data != nil {
payload = NewRawPayload(r.Data)
}
res, err := req.MakePostRequest(payload)
if err == nil {
return res.Code, res.Data, err
} else {
return -1, nil, err
}
}
func (r Request) Put() (int, []byte, error) {
req := createHttpRequest(r)
var payload Payload = nil
if r.Data != nil {
payload = NewRawPayload(r.Data)
}
res, err := req.MakePutRequest(payload)
if err == nil {
return res.Code, res.Data, err
} else {
return -1, nil, err
}
}
func (r Request) Delete() (int, []byte, error) {
req := createHttpRequest(r)
res, err := req.MakeDeleteRequest()
if err == nil {
return res.Code, res.Data, err
} else {
return -1, nil, err
}
}

View file

@ -1,104 +0,0 @@
package simplehttp
import (
"testing"
)
func TestShorthandFailingPayload(t *testing.T) {
Request{
Url: dummyurl,
Data: nil,
}.Post()
}
func TestShorthandGet(t *testing.T) {
code, _, err := Request{
Url: dummyurl,
UserAgent: "simplehttp go test",
}.Get()
if code == -1 || err != nil {
t.Fail()
}
}
func TestShorthandPost(t *testing.T) {
code, _, err := Request{
Url: dummyurl,
Data: []byte("foobar"),
UserAgent: "simplehttp go test",
Authentication: BasicAuthentication{
User: "test",
Password: "test",
},
}.Post()
if code == -1 || err != nil {
t.Fail()
}
}
func TestShorthandPut(t *testing.T) {
code, _, err := Request{
Url: dummyurl,
Data: []byte("foobar"),
UserAgent: "simplehttp go test",
}.Put()
if code == -1 || err != nil {
t.Fail()
}
}
func TestShorthandDelete(t *testing.T) {
code, _, err := Request{
Url: dummyurl,
UserAgent: "simplehttp go test",
}.Delete()
if code == -1 || err != nil {
t.Fail()
}
}
func TestFailingShorthandGet(t *testing.T) {
code, _, err := Request{
Url: invalidurl,
}.Get()
if code != -1 || err == nil {
t.Fail()
}
}
func TestFailingShorthandPost(t *testing.T) {
code, _, err := Request{
Url: invalidurl,
Data: []byte("foobar"),
}.Post()
if code != -1 || err == nil {
t.Fail()
}
}
func TestFailingShorthandPut(t *testing.T) {
code, _, err := Request{
Url: invalidurl,
Data: []byte("foobar"),
}.Put()
if code != -1 || err == nil {
t.Fail()
}
}
func TestFailingShorthandDelete(t *testing.T) {
code, _, err := Request{
Url: invalidurl,
}.Delete()
if code != -1 || err == nil {
t.Fail()
}
}

View file

@ -1,147 +0,0 @@
// Package simplehttp provides some simple methods and types to do
// HTTP queries with form values and parameters easily - especially
// if the returned result is expected to be JSON or XML.
//
// Author: Michael Banzon
package simplehttp
import (
"io"
"io/ioutil"
"net/http"
"net/url"
)
// Holds all information used to make a HTTP request.
type HTTPRequest struct {
URL string
Parameters map[string][]string
Headers map[string]string
BasicAuthUser string
BasicAuthPassword string
Client *http.Client
}
type HTTPResponse struct {
Code int
Data []byte
}
// Creates a new HTTPRequest instance.
func NewHTTPRequest(url string) *HTTPRequest {
return &HTTPRequest{URL: url, Client: http.DefaultClient}
}
// Adds a parameter to the generated query string.
func (r *HTTPRequest) AddParameter(name, value string) {
if r.Parameters == nil {
r.Parameters = make(map[string][]string)
}
r.Parameters[name] = append(r.Parameters[name], value)
}
// Adds a header that will be sent with the HTTP request.
func (r *HTTPRequest) AddHeader(name, value string) {
// hej
if r.Headers == nil {
r.Headers = make(map[string]string)
}
r.Headers[name] = value
}
// Sets username and password for basic authentication.
func (r *HTTPRequest) SetBasicAuth(user, password string) {
r.BasicAuthUser = user
r.BasicAuthPassword = password
}
func (r *HTTPRequest) MakeGetRequest() (*HTTPResponse, error) {
return r.MakeRequest("GET", nil)
}
func (r *HTTPRequest) MakePostRequest(payload Payload) (*HTTPResponse, error) {
return r.MakeRequest("POST", payload)
}
func (r *HTTPRequest) MakePutRequest(payload Payload) (*HTTPResponse, error) {
return r.MakeRequest("PUT", payload)
}
func (r *HTTPRequest) MakeDeleteRequest() (*HTTPResponse, error) {
return r.MakeRequest("DELETE", nil)
}
func (r *HTTPRequest) MakeRequest(method string, payload Payload) (*HTTPResponse, error) {
url, err := r.generateUrlWithParameters()
if err != nil {
return nil, err
}
var body io.Reader
if payload != nil {
if body, err = payload.GetPayloadBuffer(); err != nil {
return nil, err
}
} else {
body = nil
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
if payload != nil && payload.GetContentType() != "" {
req.Header.Add("Content-Type", payload.GetContentType())
}
if r.BasicAuthUser != "" && r.BasicAuthPassword != "" {
req.SetBasicAuth(r.BasicAuthUser, r.BasicAuthPassword)
}
for header, value := range r.Headers {
req.Header.Add(header, value)
}
response := HTTPResponse{}
resp, err := r.Client.Do(req)
if resp != nil {
response.Code = resp.StatusCode
}
if err != nil {
return nil, err
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
response.Data = responseBody
return &response, nil
}
// Generates the complete URL using GET parameters.
func (r *HTTPRequest) generateUrlWithParameters() (string, error) {
url, err := url.Parse(r.URL)
if err != nil {
return "", err
}
q := url.Query()
if r.Parameters != nil && len(r.Parameters) > 0 {
for name, values := range r.Parameters {
for _, value := range values {
q.Add(name, value)
}
}
}
url.RawQuery = q.Encode()
return url.String(), nil
}
func (r *HTTPRequest) SetClient(c *http.Client) {
r.Client = c
}

View file

@ -1,37 +0,0 @@
package simplehttp
import (
"github.com/mbanzon/dummyserver"
"log"
"strconv"
"testing"
)
var (
server *dummyserver.DummyServer
dummyurl string
invalidurl string
)
type testStruct struct {
Value1 string `json:"value1" xml:"value1"`
Value2 string `json:"value2" xml:"value2"`
Value3 string `json:"value3" xml:"value3"`
}
func init() {
server = dummyserver.NewRandomServer()
go func() {
err := server.Start()
log.Fatal(err)
}()
dummyurl = "http://localhost:" + strconv.Itoa(server.GetPort()) + "/"
invalidurl = "invalid://invalid"
}
func TestAddParameters(t *testing.T) {
request := NewHTTPRequest(dummyurl)
request.AddParameter("p1", "v1")
request.MakeGetRequest()
}