Merge pull request #488 from ericchiang/fix-missing-dependencies

*: update mailgun-go to remove dependency on deleted simplehttp pkg
This commit is contained in:
Eric Chiang 2016-06-28 10:28:50 -07:00 committed by GitHub
commit bdaded57b8
34 changed files with 629 additions and 1108 deletions

10
glide.lock generated
View file

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

View file

@ -38,11 +38,9 @@ import:
- package: github.com/lib/pq
version: 7175accbed18058468c07811f76440d6e8d7cf19
- package: github.com/mailgun/mailgun-go
version: 9578dc67692294bb7e2a6f4b15dd18c97af19440
version: a98c4eecda3e6a36bd804ae80e9a4f8edaa6cd99
- package: github.com/mattn/go-sqlite3
version: 2513631704612107a1c8b1803fb8e6b3dda2230e
- package: github.com/mbanzon/simplehttp
version: 04c542e7ac706a25820090f274ea6a4f39a63326
- package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/rubenv/sql-migrate

View file

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

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
"time"
)
@ -52,15 +51,16 @@ func (b Bounce) GetCode() (int, error) {
// and the slice of bounces specified, if successful.
// Note that the length of the slice may be smaller than the total number of bounces.
func (m *MailgunImpl) GetBounces(limit, skip int) (int, []Bounce, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint))
r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint))
if limit != -1 {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
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
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.
func (m *MailgunImpl) GetSingleBounce(address string) (Bounce, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var response singleBounceEnvelope
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
// code will report as a number.
func (m *MailgunImpl) AddBounce(address, code, error string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint))
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload()
payload.AddValue("address", address)
payload := newUrlEncodedPayload()
payload.addValue("address", address)
if code != "" {
payload.AddValue("code", code)
payload.addValue("code", code)
}
if error != "" {
payload.AddValue("error", error)
payload.addValue("error", error)
}
_, err := makePostRequest(r, payload)
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.
func (m *MailgunImpl) DeleteBounce(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, bouncesEndpoint) + "/" + address)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

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

View file

@ -2,7 +2,6 @@ package mailgun
import (
"fmt"
"github.com/mbanzon/simplehttp"
"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.
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 {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
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 {
TotalCount int `json:"total_count"`
Items []Credential `json:"items"`
@ -42,11 +42,12 @@ func (mg *MailgunImpl) CreateCredential(login, password string) error {
if (login == "") || (password == "") {
return ErrEmptyParam
}
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, ""))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
p.AddValue("login", login)
p.AddValue("password", password)
r := newHTTPRequest(generateCredentialsUrl(mg, ""))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
p.addValue("login", login)
p.addValue("password", password)
_, err := makePostRequest(r, p)
return err
}
@ -56,10 +57,11 @@ func (mg *MailgunImpl) ChangeCredentialPassword(id, password string) error {
if (id == "") || (password == "") {
return ErrEmptyParam
}
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, id))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
p.AddValue("password", password)
r := newHTTPRequest(generateCredentialsUrl(mg, id))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
p.addValue("password", password)
_, err := makePutRequest(r, p)
return err
}
@ -69,8 +71,9 @@ func (mg *MailgunImpl) DeleteCredential(id string) error {
if id == "" {
return ErrEmptyParam
}
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, id))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generateCredentialsUrl(mg, id))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
"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.
// 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) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r.setClient(m.Client())
if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
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
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.
func (m *MailgunImpl) GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + domain)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + domain)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var envelope singleDomainEnvelope
err := getResponseFromJSON(r, &envelope)
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,
// and as different domains if false.
func (m *MailgunImpl) CreateDomain(name string, smtpPassword string, spamAction string, wildcard bool) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint))
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
payload := simplehttp.NewUrlEncodedPayload()
payload.AddValue("name", name)
payload.AddValue("smtp_password", smtpPassword)
payload.AddValue("spam_action", spamAction)
payload.AddValue("wildcard", strconv.FormatBool(wildcard))
payload := newUrlEncodedPayload()
payload.addValue("name", name)
payload.addValue("smtp_password", smtpPassword)
payload.addValue("spam_action", spamAction)
payload.addValue("wildcard", strconv.FormatBool(wildcard))
_, err := makePostRequest(r, payload)
return err
}
// DeleteDomain instructs Mailgun to dispose of the named domain name.
func (m *MailgunImpl) DeleteDomain(name string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + name)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(domainsEndpoint) + "/" + name)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strings"
)
@ -41,9 +40,10 @@ type addressParseResult struct {
// 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.
func (m *MailgunImpl) ValidateEmail(email string) (EmailVerification, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(addressValidateEndpoint))
r.AddParameter("address", email)
r.SetBasicAuth(basicAuthUser, m.PublicApiKey())
r := newHTTPRequest(generatePublicApiUrl(addressValidateEndpoint))
r.setClient(m.Client())
r.addParameter("address", email)
r.setBasicAuth(basicAuthUser, m.PublicApiKey())
var response EmailVerification
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.
// 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) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(addressParseEndpoint))
r.AddParameter("addresses", strings.Join(addresses, ","))
r.SetBasicAuth(basicAuthUser, m.PublicApiKey())
r := newHTTPRequest(generatePublicApiUrl(addressParseEndpoint))
r.setClient(m.Client())
r.addParameter("addresses", strings.Join(addresses, ","))
r.setBasicAuth(basicAuthUser, m.PublicApiKey())
var response addressParseResult
err := getResponseFromJSON(r, &response)

View file

@ -2,7 +2,6 @@ package mailgun
import (
"fmt"
"github.com/mbanzon/simplehttp"
"time"
)
@ -58,28 +57,28 @@ func (ei *EventIterator) GetFirstPage(opts GetEventsOptions) error {
return fmt.Errorf("collation cannot at once be both ascending and descending")
}
payload := simplehttp.NewUrlEncodedPayload()
payload := newUrlEncodedPayload()
if opts.Limit != 0 {
payload.AddValue("limit", fmt.Sprintf("%d", opts.Limit))
payload.addValue("limit", fmt.Sprintf("%d", opts.Limit))
}
if opts.Compact {
payload.AddValue("pretty", "no")
payload.addValue("pretty", "no")
}
if opts.ForceAscending {
payload.AddValue("ascending", "yes")
payload.addValue("ascending", "yes")
}
if opts.ForceDescending {
payload.AddValue("ascending", "no")
payload.addValue("ascending", "no")
}
if opts.Begin != noTime {
payload.AddValue("begin", formatMailgunTime(&opts.Begin))
payload.addValue("begin", formatMailgunTime(&opts.Begin))
}
if opts.End != noTime {
payload.AddValue("end", formatMailgunTime(&opts.End))
payload.addValue("end", formatMailgunTime(&opts.End))
}
if opts.Filter != nil {
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.
// fetch completes the API fetch common to all three of these functions.
func (ei *EventIterator) fetch(url string) error {
r := simplehttp.NewHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, ei.mg.ApiKey())
r := newHTTPRequest(url)
r.setClient(ei.mg.Client())
r.setBasicAuth(basicAuthUser, ei.mg.ApiKey())
var response map[string]interface{}
err := getResponseFromJSON(r, &response)
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 (
"fmt"
"github.com/mbanzon/simplehttp"
"io"
"net/http"
"time"
)
@ -131,6 +131,8 @@ type Mailgun interface {
Domain() string
ApiKey() string
PublicApiKey() string
Client() *http.Client
SetClient(client *http.Client)
Send(m *Message) (string, string, error)
ValidateEmail(email string) (EmailVerification, error)
ParseAddresses(addresses ...string) ([]string, []string, error)
@ -195,11 +197,17 @@ type MailgunImpl struct {
domain string
apiKey string
publicApiKey string
client *http.Client
}
// NewMailGun creates a new client instance.
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
}
@ -218,6 +226,16 @@ func (m *MailgunImpl) PublicApiKey() string {
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.
func generateApiUrl(m Mailgun, endpoint string) string {
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.
func generateParameterizedUrl(m Mailgun, endpoint string, payload simplehttp.Payload) (string, error) {
paramBuffer, err := payload.GetPayloadBuffer()
func generateParameterizedUrl(m Mailgun, endpoint string, payload payload) (string, error) {
paramBuffer, err := payload.getPayloadBuffer()
if err != nil {
return "", err
}

View file

@ -1,6 +1,7 @@
package mailgun
import (
"net/http"
"strconv"
"testing"
)
@ -23,6 +24,15 @@ func TestMailgun(t *testing.T) {
if publicApiKey != m.PublicApiKey() {
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) {

View file

@ -3,7 +3,6 @@ package mailgun
import (
"encoding/json"
"fmt"
"github.com/mbanzon/simplehttp"
"strconv"
)
@ -63,17 +62,18 @@ type Member struct {
// GetLists returns the specified set of mailing lists administered by your account.
func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
r := newHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if limit != DefaultLimit {
p.AddValue("limit", strconv.Itoa(limit))
p.addValue("limit", strconv.Itoa(limit))
}
if skip != DefaultSkip {
p.AddValue("skip", strconv.Itoa(skip))
p.addValue("skip", strconv.Itoa(skip))
}
if filter != "" {
p.AddValue("address", filter)
p.addValue("address", filter)
}
var envelope struct {
Items []List `json:"items"`
@ -83,7 +83,7 @@ func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, er
if err != nil {
return -1, nil, err
}
err = response.ParseFromJSON(&envelope)
err = response.parseFromJSON(&envelope)
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,
// while AccessLevel defaults to Everyone.
func (mg *MailgunImpl) CreateList(prototype List) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
r := newHTTPRequest(generatePublicApiUrl(listsEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if prototype.Address != "" {
p.AddValue("address", prototype.Address)
p.addValue("address", prototype.Address)
}
if prototype.Name != "" {
p.AddValue("name", prototype.Name)
p.addValue("name", prototype.Name)
}
if prototype.Description != "" {
p.AddValue("description", prototype.Description)
p.addValue("description", prototype.Description)
}
if prototype.AccessLevel != "" {
p.AddValue("access_level", prototype.AccessLevel)
p.addValue("access_level", prototype.AccessLevel)
}
response, err := makePostRequest(r, p)
if err != nil {
return List{}, err
}
var l List
err = response.ParseFromJSON(&l)
err = response.parseFromJSON(&l)
return l, err
}
// 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.
func (mg *MailgunImpl) DeleteList(addr string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
return err
}
@ -129,13 +131,14 @@ func (mg *MailgunImpl) DeleteList(addr string) error {
// GetListByAddress allows your application to recover the complete List structure
// representing a mailing list, so long as you have its e-mail address.
func (mg *MailgunImpl) GetListByAddress(addr string) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
response, err := makeGetRequest(r)
var envelope struct {
List `json:"list"`
}
err = response.ParseFromJSON(&envelope)
err = response.parseFromJSON(&envelope)
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.
// Make sure you account for the change accordingly.
func (mg *MailgunImpl) UpdateList(addr string, prototype List) (List, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
r := newHTTPRequest(generatePublicApiUrl(listsEndpoint) + "/" + addr)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if prototype.Address != "" {
p.AddValue("address", prototype.Address)
p.addValue("address", prototype.Address)
}
if prototype.Name != "" {
p.AddValue("name", prototype.Name)
p.addValue("name", prototype.Name)
}
if prototype.Description != "" {
p.AddValue("description", prototype.Description)
p.addValue("description", prototype.Description)
}
if prototype.AccessLevel != "" {
p.AddValue("access_level", prototype.AccessLevel)
p.addValue("access_level", prototype.AccessLevel)
}
var l List
response, err := makePutRequest(r, p)
if err != nil {
return l, err
}
err = response.ParseFromJSON(&l)
err = response.parseFromJSON(&l)
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
// Subscribed and Unsubscribed indicate you want only those eponymous subsets.
func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, []Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if limit != DefaultLimit {
p.AddValue("limit", strconv.Itoa(limit))
p.addValue("limit", strconv.Itoa(limit))
}
if skip != DefaultSkip {
p.AddValue("skip", strconv.Itoa(skip))
p.addValue("skip", strconv.Itoa(skip))
}
if s != nil {
p.AddValue("subscribed", yesNo(*s))
p.addValue("subscribed", yesNo(*s))
}
var envelope struct {
TotalCount int `json:"total_count"`
@ -196,15 +201,16 @@ func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, [
if err != nil {
return -1, nil, err
}
err = response.ParseFromJSON(&envelope)
err = response.parseFromJSON(&envelope)
return envelope.TotalCount, envelope.Items, err
}
// GetMemberByAddress returns a complete Member structure for a member of a mailing list,
// given only their subscription e-mail address.
func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
response, err := makeGetRequest(r)
if err != nil {
return Member{}, err
@ -212,7 +218,7 @@ func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) {
var envelope struct {
Member Member `json:"member"`
}
err = response.ParseFromJSON(&envelope)
err = response.parseFromJSON(&envelope)
return envelope.Member, err
}
@ -225,15 +231,16 @@ func (mg *MailgunImpl) CreateMember(merge bool, addr string, prototype Member) e
return err
}
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewFormDataPayload()
p.AddValue("upsert", yesNo(merge))
p.AddValue("address", prototype.Address)
p.AddValue("name", prototype.Name)
p.AddValue("vars", string(vs))
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newFormDataPayload()
p.addValue("upsert", yesNo(merge))
p.addValue("address", prototype.Address)
p.addValue("name", prototype.Name)
p.addValue("vars", string(vs))
if prototype.Subscribed != nil {
p.AddValue("subscribed", yesNo(*prototype.Subscribed))
p.addValue("subscribed", yesNo(*prototype.Subscribed))
}
_, err = makePostRequest(r, p)
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.
// Address, Name, Vars, and Subscribed fields may be changed.
func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, error) {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewFormDataPayload()
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newFormDataPayload()
if prototype.Address != "" {
p.AddValue("address", prototype.Address)
p.addValue("address", prototype.Address)
}
if prototype.Name != "" {
p.AddValue("name", prototype.Name)
p.addValue("name", prototype.Name)
}
if prototype.Vars != nil {
vs, err := json.Marshal(prototype.Vars)
if err != nil {
return Member{}, err
}
p.AddValue("vars", string(vs))
p.addValue("vars", string(vs))
}
if prototype.Subscribed != nil {
p.AddValue("subscribed", yesNo(*prototype.Subscribed))
p.addValue("subscribed", yesNo(*prototype.Subscribed))
}
response, err := makePutRequest(r, p)
if err != nil {
@ -268,14 +276,15 @@ func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, erro
var envelope struct {
Member Member `json:"member"`
}
err = response.ParseFromJSON(&envelope)
err = response.parseFromJSON(&envelope)
return envelope.Member, err
}
// DeleteMember removes the member from the list.
func (mg *MailgunImpl) DeleteMember(member, addr string) error {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + "/" + member)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + "/" + member)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
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.
// Other fields are optional, but may be set according to your needs.
func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error {
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewFormDataPayload()
r := newHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newFormDataPayload()
if s != nil {
p.AddValue("subscribed", yesNo(*s))
p.addValue("subscribed", yesNo(*s))
}
bs, err := json.Marshal(newMembers)
if err != nil {
return err
}
fmt.Println(string(bs))
p.AddValue("members", string(bs))
p.addValue("members", string(bs))
_, err = makePostRequest(r, p)
return err
}

View file

@ -5,8 +5,6 @@ import (
"errors"
"io"
"time"
"github.com/mbanzon/simplehttp"
)
// 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)
addBCC(string)
setHtml(string)
addValues(*simplehttp.FormDataPayload)
addValues(*formDataPayload)
isValid() bool
endpoint() string
recipientCount() int
@ -423,44 +421,44 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
if !isValid(message) {
err = errors.New("Message not valid")
} else {
payload := simplehttp.NewFormDataPayload()
payload := newFormDataPayload()
message.specific.addValues(payload)
for _, to := range message.to {
payload.AddValue("to", to)
payload.addValue("to", to)
}
for _, tag := range message.tags {
payload.AddValue("o:tag", tag)
payload.addValue("o:tag", tag)
}
for _, campaign := range message.campaigns {
payload.AddValue("o:campaign", campaign)
payload.addValue("o:campaign", campaign)
}
if message.dkimSet {
payload.AddValue("o:dkim", yesNo(message.dkim))
payload.addValue("o:dkim", yesNo(message.dkim))
}
if message.deliveryTime != nil {
payload.AddValue("o:deliverytime", formatMailgunTime(message.deliveryTime))
payload.addValue("o:deliverytime", formatMailgunTime(message.deliveryTime))
}
if message.testMode {
payload.AddValue("o:testmode", "yes")
payload.addValue("o:testmode", "yes")
}
if message.trackingSet {
payload.AddValue("o:tracking", yesNo(message.tracking))
payload.addValue("o:tracking", yesNo(message.tracking))
}
if message.trackingClicksSet {
payload.AddValue("o:tracking-clicks", yesNo(message.trackingClicks))
payload.addValue("o:tracking-clicks", yesNo(message.trackingClicks))
}
if message.trackingOpensSet {
payload.AddValue("o:tracking-opens", yesNo(message.trackingOpens))
payload.addValue("o:tracking-opens", yesNo(message.trackingOpens))
}
if message.headers != nil {
for header, value := range message.headers {
payload.AddValue("h:"+header, value)
payload.addValue("h:"+header, value)
}
}
if message.variables != nil {
for variable, value := range message.variables {
payload.AddValue("v:"+variable, value)
payload.addValue("v:"+variable, value)
}
}
if message.recipientVariables != nil {
@ -468,26 +466,27 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
if err != nil {
return "", "", err
}
payload.AddValue("recipient-variables", string(j))
payload.addValue("recipient-variables", string(j))
}
if message.attachments != nil {
for _, attachment := range message.attachments {
payload.AddFile("attachment", attachment)
payload.addFile("attachment", attachment)
}
}
if message.readerAttachments != nil {
for _, readerAttachment := range message.readerAttachments {
payload.AddReadCloser("attachment", readerAttachment.Filename, readerAttachment.ReadCloser)
payload.addReadCloser("attachment", readerAttachment.Filename, readerAttachment.ReadCloser)
}
}
if message.inlines != nil {
for _, inline := range message.inlines {
payload.AddFile("inline", inline)
payload.addFile("inline", inline)
}
}
r := simplehttp.NewHTTPRequest(generateApiUrl(m, message.specific.endpoint()))
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, message.specific.endpoint()))
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var response sendMessageResponse
err = postResponseFromJSON(r, payload, &response)
@ -500,23 +499,23 @@ func (m *MailgunImpl) Send(message *Message) (mes string, id string, err error)
return
}
func (pm *plainMessage) addValues(p *simplehttp.FormDataPayload) {
p.AddValue("from", pm.from)
p.AddValue("subject", pm.subject)
p.AddValue("text", pm.text)
func (pm *plainMessage) addValues(p *formDataPayload) {
p.addValue("from", pm.from)
p.addValue("subject", pm.subject)
p.addValue("text", pm.text)
for _, cc := range pm.cc {
p.AddValue("cc", cc)
p.addValue("cc", cc)
}
for _, bcc := range pm.bcc {
p.AddValue("bcc", bcc)
p.addValue("bcc", bcc)
}
if pm.html != "" {
p.AddValue("html", pm.html)
p.addValue("html", pm.html)
}
}
func (mm *mimeMessage) addValues(p *simplehttp.FormDataPayload) {
p.AddReadCloser("message", "message.mime", mm.body)
func (mm *mimeMessage) addValues(p *formDataPayload) {
p.addReadCloser("message", "message.mime", mm.body)
}
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.
func (mg *MailgunImpl) GetStoredMessage(id string) (StoredMessage, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var response StoredMessage
err := getResponseFromJSON(r, &response)
@ -625,9 +625,10 @@ func (mg *MailgunImpl) GetStoredMessage(id string) (StoredMessage, error) {
// thus delegates to the caller the required parsing.
func (mg *MailgunImpl) GetStoredMessageRaw(id string) (StoredMessageRaw, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r.AddHeader("Accept", "message/rfc2822")
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
r.addHeader("Accept", "message/rfc2822")
var response StoredMessageRaw
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.
func (mg *MailgunImpl) DeleteStoredMessage(id string) error {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := simplehttp.NewHTTPRequest(url)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

@ -2,7 +2,6 @@ package mailgun
import (
"fmt"
"github.com/mbanzon/simplehttp"
)
// The MailgunGoUserAgent identifies the client to the server, for logging purposes.
@ -17,12 +16,13 @@ type UnexpectedResponseError struct {
Expected []int
Actual int
URL string
Data []byte
}
// String() converts the error into a human-readable, logfmt-compliant string.
// See http://godoc.org/github.com/kr/logfmt for details on logfmt formatting.
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().
@ -31,11 +31,12 @@ func (e *UnexpectedResponseError) Error() string {
}
// 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{
URL: url,
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.
// See simplehttp.MakeRequest for more details.
func makeRequest(r *simplehttp.HTTPRequest, kind string, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeRequest(kind, p)
func makeRequest(r *httpRequest, kind string, p payload) (*httpResponse, error) {
r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.makeRequest(kind, p)
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
}
// getResponseFromJSON shim performs a GET request, checking for a positive outcome.
// See simplehttp.GetResponseFromJSON for more details.
func getResponseFromJSON(r *simplehttp.HTTPRequest, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakeGetRequest()
func getResponseFromJSON(r *httpRequest, v interface{}) error {
r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.makeGetRequest()
if err != nil {
return err
}
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.
// See simplehttp.PostResponseFromJSON for more details.
func postResponseFromJSON(r *simplehttp.HTTPRequest, p simplehttp.Payload, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakePostRequest(p)
func postResponseFromJSON(r *httpRequest, p payload, v interface{}) error {
r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.makePostRequest(p)
if err != nil {
return err
}
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.
// See simplehttp.PutResponseFromJSON for more details.
func putResponseFromJSON(r *simplehttp.HTTPRequest, p simplehttp.Payload, v interface{}) error {
r.AddHeader("User-Agent", MailgunGoUserAgent)
response, err := r.MakePutRequest(p)
func putResponseFromJSON(r *httpRequest, p payload, v interface{}) error {
r.addHeader("User-Agent", MailgunGoUserAgent)
response, err := r.makePutRequest(p)
if err != nil {
return err
}
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.
// See simplehttp.MakeGetRequest for more details.
func makeGetRequest(r *simplehttp.HTTPRequest) (*simplehttp.HTTPResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeGetRequest()
func makeGetRequest(r *httpRequest) (*httpResponse, error) {
r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.makeGetRequest()
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
}
// makePostRequest shim performs a POST request, checking for a positive outcome.
// See simplehttp.MakePostRequest for more details.
func makePostRequest(r *simplehttp.HTTPRequest, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakePostRequest(p)
func makePostRequest(r *httpRequest, p payload) (*httpResponse, error) {
r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.makePostRequest(p)
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
}
// makePutRequest shim performs a PUT request, checking for a positive outcome.
// See simplehttp.MakePutRequest for more details.
func makePutRequest(r *simplehttp.HTTPRequest, p simplehttp.Payload) (*simplehttp.HTTPResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakePutRequest(p)
func makePutRequest(r *httpRequest, p payload) (*httpResponse, error) {
r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.makePutRequest(p)
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
}
// makeDeleteRequest shim performs a DELETE request, checking for a positive outcome.
// See simplehttp.MakeDeleteRequest for more details.
func makeDeleteRequest(r *simplehttp.HTTPRequest) (*simplehttp.HTTPResponse, error) {
r.AddHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.MakeDeleteRequest()
func makeDeleteRequest(r *httpRequest) (*httpResponse, error) {
r.addHeader("User-Agent", MailgunGoUserAgent)
rsp, err := r.makeDeleteRequest()
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
}

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
)
@ -34,14 +33,15 @@ type Route struct {
// messages sent to a specfic address on your domain.
// See the Mailgun documentation for more information.
func (mg *MailgunImpl) GetRoutes(limit, skip int) (int, []Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint))
r := newHTTPRequest(generatePublicApiUrl(routesEndpoint))
if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
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 {
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.
// See the Route structure definition for more details.
func (mg *MailgunImpl) CreateRoute(prototype Route) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
p.AddValue("priority", strconv.Itoa(prototype.Priority))
p.AddValue("description", prototype.Description)
p.AddValue("expression", prototype.Expression)
r := newHTTPRequest(generatePublicApiUrl(routesEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
p.addValue("priority", strconv.Itoa(prototype.Priority))
p.addValue("description", prototype.Description)
p.addValue("expression", prototype.Expression)
for _, action := range prototype.Actions {
p.AddValue("action", action)
p.addValue("action", action)
}
var envelope struct {
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.
// See the Route structure definition and the Mailgun API documentation for more details.
func (mg *MailgunImpl) DeleteRoute(id string) error {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
return err
}
// GetRouteByID retrieves the complete route definition associated with the unique route ID.
func (mg *MailgunImpl) GetRouteByID(id string) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct {
Message string `json:"message"`
*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.
// All other fields remain as-is.
func (mg *MailgunImpl) UpdateRoute(id string, route Route) (Route, error) {
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
r := newHTTPRequest(generatePublicApiUrl(routesEndpoint) + "/" + id)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
if route.Priority != 0 {
p.AddValue("priority", strconv.Itoa(route.Priority))
p.addValue("priority", strconv.Itoa(route.Priority))
}
if route.Description != "" {
p.AddValue("description", route.Description)
p.addValue("description", route.Description)
}
if route.Expression != "" {
p.AddValue("expression", route.Expression)
p.addValue("expression", route.Expression)
}
if route.Actions != nil {
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.

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
)
@ -28,14 +27,15 @@ type complaintsEnvelope struct {
// Recipients of your messages can click on a link which sends feedback to Mailgun
// indicating that the message they received is, to them, spam.
func (m *MailgunImpl) GetComplaints(limit, skip int) (int, []Complaint, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
if limit != -1 {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
if skip != -1 {
r.AddParameter("skip", strconv.Itoa(skip))
r.addParameter("skip", strconv.Itoa(skip))
}
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.
// If no complaint exists, the Complaint instance returned will be empty.
func (m *MailgunImpl) GetSingleComplaint(address string) (Complaint, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
var c Complaint
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
// from your domain.
func (m *MailgunImpl) CreateComplaint(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.SetBasicAuth(basicAuthUser, m.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
p.AddValue("address", address)
r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint))
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
p := newUrlEncodedPayload()
p.addValue("address", address)
_, err := makePostRequest(r, p)
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
// of receiving spam from your domain.
func (m *MailgunImpl) DeleteComplaint(address string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, complaintsEndpoint) + "/" + address)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
"time"
)
@ -23,23 +22,24 @@ type statsEnvelope struct {
// 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.
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 {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
if skip != -1 {
r.AddParameter("skip", strconv.Itoa(skip))
r.addParameter("skip", strconv.Itoa(skip))
}
if startDate != nil {
r.AddParameter("start-date", startDate.Format(time.RFC3339))
r.addParameter("start-date", startDate.Format(time.RFC3339))
}
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
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.
func (m *MailgunImpl) DeleteTag(tag string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(m, deleteTagEndpoint) + "/" + tag)
r.SetBasicAuth(basicAuthUser, m.ApiKey())
r := newHTTPRequest(generateApiUrl(m, deleteTagEndpoint) + "/" + tag)
r.setClient(m.Client())
r.setBasicAuth(basicAuthUser, m.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

@ -1,7 +1,6 @@
package mailgun
import (
"github.com/mbanzon/simplehttp"
"strconv"
)
@ -15,14 +14,15 @@ type Unsubscription struct {
// GetUnsubscribes retrieves a list of unsubscriptions issued by recipients of mail from your domain.
// Zero is a valid list length.
func (mg *MailgunImpl) GetUnsubscribes(limit, skip int) (int, []Unsubscription, error) {
r := simplehttp.NewHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
if limit != DefaultLimit {
r.AddParameter("limit", strconv.Itoa(limit))
r.addParameter("limit", strconv.Itoa(limit))
}
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 {
TotalCount int `json:"total_count"`
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.
// Zero is a valid list length.
func (mg *MailgunImpl) GetUnsubscribesByAddress(a string) (int, []Unsubscription, error) {
r := simplehttp.NewHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
var envelope struct {
TotalCount int `json:"total_count"`
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.
func (mg *MailgunImpl) Unsubscribe(a, t string) error {
r := simplehttp.NewHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
p := simplehttp.NewUrlEncodedPayload()
p.AddValue("address", a)
p.AddValue("tag", t)
r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
p := newUrlEncodedPayload()
p.addValue("address", a)
p.addValue("tag", t)
_, err := makePostRequest(r, p)
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
// with the given ID will be removed.
func (mg *MailgunImpl) RemoveUnsubscribe(a string) error {
r := simplehttp.NewHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.ApiKey())
_, err := makeDeleteRequest(r)
return err
}

View file

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