Rough in mailgun support
This commit is contained in:
parent
c78998739d
commit
2f0fd351a8
51 changed files with 4749 additions and 5 deletions
8
Godeps/Godeps.json
generated
8
Godeps/Godeps.json
generated
|
@ -37,6 +37,14 @@
|
|||
"Comment": "go1.0-cutoff-29-g30ed220",
|
||||
"Rev": "30ed2200d7ec99cf17272292f1d4b7b0bd7165db"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mailgun/mailgun-go",
|
||||
"Rev": "9578dc67692294bb7e2a6f4b15dd18c97af19440"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mbanzon/simplehttp",
|
||||
"Rev": "04c542e7ac706a25820090f274ea6a4f39a63326"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/thermokarst/jwt",
|
||||
"Rev": "66ca404d841ed908aa6cc9361107b0c363c94cd8"
|
||||
|
|
10
Godeps/_workspace/src/github.com/mailgun/mailgun-go/.travis.yml
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/mailgun/mailgun-go/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.1.2
|
||||
- 1.2
|
||||
- tip
|
||||
env:
|
||||
- GOARCH=amd64
|
||||
- GOARCH=386
|
||||
script:
|
||||
- go test
|
27
Godeps/_workspace/src/github.com/mailgun/mailgun-go/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/mailgun/mailgun-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
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 names of Mailgun, Michael Banzon, 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.
|
20
Godeps/_workspace/src/github.com/mailgun/mailgun-go/README.md
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/mailgun/mailgun-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Mailgun with Go
|
||||
===============
|
||||
|
||||
[](https://travis-ci.org/mbanzon/mailgun)
|
||||
[](https://godoc.org/github.com/mailgun/mailgun-go)
|
||||
|
||||
Go library for sending mail with the Mailgun API.
|
||||
|
||||
|
||||
See these examples on how how to use use the library with various parts of the Mailgun API:
|
||||
|
||||
* [Messages](https://gist.github.com/mbanzon/8179682 "mailgun-message-example.go")
|
||||
* [E-mail validation](https://gist.github.com/mbanzon/8179989 "mailgun-validation-example.go")
|
||||
* [Bounces](https://gist.github.com/mbanzon/8179951 "mailgun-bounces-example.go")
|
||||
* [Stats](https://gist.github.com/mbanzon/8206266 "mailgun-stats-example.go")
|
||||
* [File Attachment from Memory](https://gist.github.com/sym3tri/8a29ddecd65ec4f8ccfc)
|
||||
|
||||
More examples are coming soon.
|
||||
|
||||
The code is released under a 3-clause BSD license. See the LICENSE file for more information.
|
29
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/acceptance.go
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/acceptance.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// +build acceptance
|
||||
|
||||
// The acceptance test package includes utilities supporting acceptance tests in *_test.go
|
||||
// files. To execute these acceptance tests, you must invoke them using the acceptance
|
||||
// build tag, like so:
|
||||
//
|
||||
// $ go test -tags acceptance github.com/mailgun/mailgun-go
|
||||
//
|
||||
// Note that some API calls may potentially cost the user money! By default, such tests
|
||||
// do NOT run. However, you will then not be testing the full capability of Mailgun.
|
||||
// To run them, you'll also need to specify the spendMoney build tag:
|
||||
//
|
||||
// $ go test -tags "acceptance spendMoney" github.com/mailgun/mailgun-go
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Many tests require configuration settings unique to the user, passed in via
|
||||
// environment variables. If these variables aren't set, we need to fail the test early.
|
||||
func reqEnv(t *testing.T, variableName string) string {
|
||||
value := os.Getenv(variableName)
|
||||
if value == "" {
|
||||
t.Fatalf("Expected environment variable %s to be set", variableName)
|
||||
}
|
||||
return value
|
||||
}
|
119
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/bounces_test.go
generated
vendored
Normal file
119
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/bounces_test.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBounces(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
n, bounces, err := mg.GetBounces(-1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n > 0 {
|
||||
t.Fatal("Expected no bounces for what should be a clean domain.")
|
||||
}
|
||||
if n != len(bounces) {
|
||||
t.Fatalf("Expected length of bounces %d to equal returned length %d", len(bounces), n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSingleBounce(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
exampleEmail := fmt.Sprintf("baz@%s", domain)
|
||||
_, err := mg.GetSingleBounce(exampleEmail)
|
||||
if err == nil {
|
||||
t.Fatal("Did not expect a bounce to exist")
|
||||
}
|
||||
ure, ok := err.(*mailgun.UnexpectedResponseError)
|
||||
if !ok {
|
||||
t.Fatal("Expected UnexpectedResponseError")
|
||||
}
|
||||
if ure.Actual != 404 {
|
||||
t.Fatalf("Expected 404 response code; got %d", ure.Actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDelBounces(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
// Compute an e-mail address for our domain.
|
||||
|
||||
exampleEmail := fmt.Sprintf("baz@%s", domain)
|
||||
|
||||
// First, basic sanity check.
|
||||
// Fail early if we have bounces for a fictitious e-mail address.
|
||||
|
||||
n, _, err := mg.GetBounces(-1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n > 0 {
|
||||
t.Fatal("Expected no bounces for what should be a clean domain.")
|
||||
}
|
||||
|
||||
bounce, err := mg.GetSingleBounce(exampleEmail)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected no bounces for %s", exampleEmail)
|
||||
}
|
||||
|
||||
// Add the bounce for our address.
|
||||
|
||||
err = mg.AddBounce(exampleEmail, "550", "TestAddDelBounces-generated error")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// We should now have one bounce listed when we query the API.
|
||||
|
||||
n, bounces, err := mg.GetBounces(-1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 1 {
|
||||
t.Fatal("Expected one bounce for this domain.")
|
||||
}
|
||||
if bounces[0].Address != exampleEmail {
|
||||
t.Fatalf("Expected bounce for address %s; got %s", exampleEmail, bounces[0].Address)
|
||||
}
|
||||
|
||||
bounce, err = mg.GetSingleBounce(exampleEmail)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bounce.CreatedAt == "" {
|
||||
t.Fatalf("Expected at least one bounce for %s", exampleEmail)
|
||||
}
|
||||
|
||||
// Delete it. This should put us back the way we were.
|
||||
|
||||
err = mg.DeleteBounce(exampleEmail)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Make sure we're back to the way we were.
|
||||
|
||||
n, _, err = mg.GetBounces(-1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n > 0 {
|
||||
t.Fatal("Expected no bounces for what should be a clean domain.")
|
||||
}
|
||||
|
||||
_, err = mg.GetSingleBounce(exampleEmail)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected no bounces for %s", exampleEmail)
|
||||
}
|
||||
}
|
53
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/credentials_test.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/credentials_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"os"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func TestGetCredentials(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
n, cs, err := mg.GetCredentials(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tw := &tabwriter.Writer{}
|
||||
tw.Init(os.Stdout, 2, 8, 2, ' ', 0)
|
||||
fmt.Fprintf(tw, "Login\tCreated At\t\n")
|
||||
for _, c := range cs {
|
||||
fmt.Fprintf(tw, "%s\t%s\t\n", c.Login, c.CreatedAt)
|
||||
}
|
||||
tw.Flush()
|
||||
fmt.Printf("%d credentials listed out of %d\n", len(cs), n)
|
||||
}
|
||||
|
||||
func TestCreateDeleteCredentials(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
randomPassword := randomString(16, "pw")
|
||||
randomID := randomString(16, "usr")
|
||||
randomLogin := fmt.Sprintf("%s@%s", randomID, domain)
|
||||
|
||||
err := mg.CreateCredential(randomLogin, randomPassword)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = mg.ChangeCredentialPassword(randomID, randomString(16, "pw2"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = mg.DeleteCredential(randomID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
96
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/domains_test.go
generated
vendored
Normal file
96
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/domains_test.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetDomains(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
n, domains, err := mg.GetDomains(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("TestGetDomains: %d domains retrieved\n", n)
|
||||
for _, d := range domains {
|
||||
fmt.Printf("TestGetDomains: %#v\n", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSingleDomain(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
_, domains, err := mg.GetDomains(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dr, rxDnsRecords, txDnsRecords, err := mg.GetSingleDomain(domains[0].Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("TestGetSingleDomain: %#v\n", dr)
|
||||
for _, rxd := range rxDnsRecords {
|
||||
fmt.Printf("TestGetSingleDomains: %#v\n", rxd)
|
||||
}
|
||||
for _, txd := range txDnsRecords {
|
||||
fmt.Printf("TestGetSingleDomains: %#v\n", txd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSingleDomainNotExist(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
_, _, _, err := mg.GetSingleDomain(randomString(32, "com.edu.org.")+".com")
|
||||
if err == nil {
|
||||
t.Fatal("Did not expect a domain to exist")
|
||||
}
|
||||
ure, ok := err.(*mailgun.UnexpectedResponseError)
|
||||
if !ok {
|
||||
t.Fatal("Expected UnexpectedResponseError")
|
||||
}
|
||||
if ure.Actual != 404 {
|
||||
t.Fatalf("Expected 404 response code; got %d", ure.Actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDeleteDomain(t *testing.T) {
|
||||
// First, we need to add the domain.
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
randomDomainName := randomString(16, "DOMAIN") + ".example.com"
|
||||
randomPassword := randomString(16, "PASSWD")
|
||||
err := mg.CreateDomain(randomDomainName, randomPassword, mailgun.Tag, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Next, we delete it.
|
||||
err = mg.DeleteDomain(randomDomainName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// randomString generates a string of given length, but random content.
|
||||
// All content will be within the ASCII graphic character set.
|
||||
// (Implementation from Even Shaw's contribution on
|
||||
// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
|
||||
func randomString(n int, prefix string) string {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
var bytes = make([]byte, n)
|
||||
rand.Read(bytes)
|
||||
for i, b := range bytes {
|
||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||
}
|
||||
return prefix + string(bytes)
|
||||
}
|
52
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/email_validation_test.go
generated
vendored
Normal file
52
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/email_validation_test.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmailValidation(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, "", apiKey)
|
||||
ev, err := mg.ValidateEmail("foo@mailgun.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ev.IsValid != true {
|
||||
t.Fatal("Expected a valid e-mail address")
|
||||
}
|
||||
if ev.Parts.DisplayName != "" {
|
||||
t.Fatal("No display name should exist")
|
||||
}
|
||||
if ev.Parts.LocalPart != "foo" {
|
||||
t.Fatal("Expected local part of foo; got ", ev.Parts.LocalPart)
|
||||
}
|
||||
if ev.Parts.Domain != "mailgun.com" {
|
||||
t.Fatal("Expected mailgun.com domain; got ", ev.Parts.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAddresses(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, "", apiKey)
|
||||
addressesThatParsed, unparsableAddresses, err := mg.ParseAddresses("Alice <alice@example.com>", "bob@example.com", "example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hittest := map[string]bool{
|
||||
"Alice <alice@example.com>": true,
|
||||
"bob@example.com": true,
|
||||
}
|
||||
for _, a := range addressesThatParsed {
|
||||
if !hittest[a] {
|
||||
t.Fatalf("Expected %s to be parsable", a)
|
||||
}
|
||||
}
|
||||
if len(unparsableAddresses) != 1 {
|
||||
t.Fatalf("Expected 1 address to be unparsable; got %d", len(unparsableAddresses))
|
||||
}
|
||||
}
|
41
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/events_test.go
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/events_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"os"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func TestEventIterator(t *testing.T) {
|
||||
// Grab the list of events (as many as we can get)
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
ei := mg.NewEventIterator()
|
||||
err := ei.GetFirstPage(mailgun.GetEventsOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Print out the kind of event and timestamp.
|
||||
// Specifics about each event will depend on the "event" type.
|
||||
events := ei.Events()
|
||||
tw := &tabwriter.Writer{}
|
||||
tw.Init(os.Stdout, 2, 8, 2, ' ', tabwriter.AlignRight)
|
||||
fmt.Fprintln(tw, "Event\tTimestamp\t")
|
||||
for _, event := range events {
|
||||
fmt.Fprintf(tw, "%s\t%v\t\n", event["event"], event["timestamp"])
|
||||
}
|
||||
tw.Flush()
|
||||
fmt.Printf("%d events dumped\n\n", len(events))
|
||||
|
||||
// We're on the first page. We must at the beginning.
|
||||
ei.GetPrevious()
|
||||
if len(ei.Events()) != 0 {
|
||||
t.Fatal("Expected to be at the beginning")
|
||||
}
|
||||
}
|
205
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/mailing_lists_test.go
generated
vendored
Normal file
205
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/mailing_lists_test.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
// +build acceptance,spendMoney
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (mailgun.Mailgun, string) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
address := fmt.Sprintf("list5@%s", domain)
|
||||
_, err := mg.CreateList(mailgun.List{
|
||||
Address: address,
|
||||
Name: address,
|
||||
Description: "TestMailingListMembers-related mailing list",
|
||||
AccessLevel: mailgun.Members,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return mg, address
|
||||
}
|
||||
|
||||
func teardown(t *testing.T, mg mailgun.Mailgun, address string) {
|
||||
err := mg.DeleteList(address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailingListMembers(t *testing.T) {
|
||||
mg, address := setup(t)
|
||||
defer teardown(t, mg, address)
|
||||
|
||||
var countPeople = func() int {
|
||||
n, _, err := mg.GetMembers(mailgun.DefaultLimit, mailgun.DefaultSkip, mailgun.All, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
startCount := countPeople()
|
||||
protoJoe := mailgun.Member{
|
||||
Address: "joe@example.com",
|
||||
Name: "Joe Example",
|
||||
Subscribed: mailgun.Subscribed,
|
||||
}
|
||||
err := mg.CreateMember(true, address, protoJoe)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newCount := countPeople()
|
||||
if newCount <= startCount {
|
||||
t.Fatalf("Expected %d people subscribed; got %d", startCount+1, newCount)
|
||||
}
|
||||
|
||||
theMember, err := mg.GetMemberByAddress("joe@example.com", address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if (theMember.Address != protoJoe.Address) ||
|
||||
(theMember.Name != protoJoe.Name) ||
|
||||
(*theMember.Subscribed != *protoJoe.Subscribed) ||
|
||||
(len(theMember.Vars) != 0) {
|
||||
t.Fatalf("Unexpected Member: Expected [%#v], Got [%#v]", protoJoe, theMember)
|
||||
}
|
||||
|
||||
_, err = mg.UpdateMember("joe@example.com", address, mailgun.Member{
|
||||
Name: "Joe Cool",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
theMember, err = mg.GetMemberByAddress("joe@example.com", address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if theMember.Name != "Joe Cool" {
|
||||
t.Fatal("Expected Joe Cool; got " + theMember.Name)
|
||||
}
|
||||
|
||||
err = mg.DeleteMember("joe@example.com", address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if countPeople() != startCount {
|
||||
t.Fatalf("Expected %d people; got %d instead", startCount, countPeople())
|
||||
}
|
||||
|
||||
err = mg.CreateMemberList(nil, address, []interface{}{
|
||||
mailgun.Member{
|
||||
Address: "joe.user1@example.com",
|
||||
Name: "Joe's debugging account",
|
||||
Subscribed: mailgun.Unsubscribed,
|
||||
},
|
||||
mailgun.Member{
|
||||
Address: "Joe Cool <joe.user2@example.com>",
|
||||
Name: "Joe's Cool Account",
|
||||
Subscribed: mailgun.Subscribed,
|
||||
},
|
||||
mailgun.Member{
|
||||
Address: "joe.user3@example.com",
|
||||
Vars: map[string]interface{}{
|
||||
"packet-email": "KW9ABC @ BOGBBS-4.#NCA.CA.USA.NOAM",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
theMember, err = mg.GetMemberByAddress("joe.user2@example.com", address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if theMember.Name != "Joe's Cool Account" {
|
||||
t.Fatalf("Expected Joe's Cool Account; got %s", theMember.Name)
|
||||
}
|
||||
if theMember.Subscribed != nil {
|
||||
if *theMember.Subscribed != true {
|
||||
t.Fatalf("Expected subscribed to be true; got %v", *theMember.Subscribed)
|
||||
}
|
||||
} else {
|
||||
t.Fatal("Expected some kind of subscription status; got nil.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailingLists(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
listAddr := fmt.Sprintf("list2@%s", domain)
|
||||
protoList := mailgun.List{
|
||||
Address: listAddr,
|
||||
Name: "List1",
|
||||
Description: "A list created by an acceptance test.",
|
||||
AccessLevel: mailgun.Members,
|
||||
}
|
||||
|
||||
var countLists = func() int {
|
||||
total, _, err := mg.GetLists(mailgun.DefaultLimit, mailgun.DefaultSkip, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
startCount := countLists()
|
||||
|
||||
_, err := mg.CreateList(protoList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
err = mg.DeleteList(listAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newCount := countLists()
|
||||
if newCount != startCount {
|
||||
t.Fatalf("Expected %d lists defined; got %d", startCount, newCount)
|
||||
}
|
||||
}()
|
||||
|
||||
newCount := countLists()
|
||||
if newCount <= startCount {
|
||||
t.Fatalf("Expected %d lists defined; got %d", startCount+1, newCount)
|
||||
}
|
||||
|
||||
theList, err := mg.GetListByAddress(listAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
protoList.CreatedAt = theList.CreatedAt // ignore this field when comparing.
|
||||
if theList != protoList {
|
||||
t.Fatalf("Unexpected list descriptor: Expected [%#v], Got [%#v]", protoList, theList)
|
||||
}
|
||||
|
||||
_, err = mg.UpdateList(listAddr, mailgun.List{
|
||||
Description: "A list whose description changed",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
theList, err = mg.GetListByAddress(listAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newList := protoList
|
||||
newList.Description = "A list whose description changed"
|
||||
if theList != newList {
|
||||
t.Fatalf("Expected [%#v], Got [%#v]", newList, theList)
|
||||
}
|
||||
}
|
329
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/messages_test.go
generated
vendored
Normal file
329
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/messages_test.go
generated
vendored
Normal file
|
@ -0,0 +1,329 @@
|
|||
// +build acceptance,spendMoney
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
fromUser = "=?utf-8?q?Katie_Brewer=2C_CFP=C2=AE?= <joe@example.com>"
|
||||
exampleSubject = "Joe's Example Subject"
|
||||
exampleText = "Testing some Mailgun awesomeness!"
|
||||
exampleHtml = "<html><head /><body><p>Testing some <a href=\"http://google.com?q=abc&r=def&s=ghi\">Mailgun HTML awesomeness!</a> at www.kc5tja@yahoo.com</p></body></html>"
|
||||
|
||||
exampleMime = `Content-Type: text/plain; charset="ascii"
|
||||
Subject: Joe's Example Subject
|
||||
From: Joe Example <joe@example.com>
|
||||
To: BARGLEGARF <sam.falvo@rackspace.com>
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Date: Thu, 6 Mar 2014 00:37:52 +0000
|
||||
|
||||
Testing some Mailgun MIME awesomeness!
|
||||
`
|
||||
templateText = "Greetings %recipient.name%! Your reserved seat is at table %recipient.table%."
|
||||
)
|
||||
|
||||
func TestSendLegacyPlain(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlain:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyPlainWithTracking(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetTracking(true)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlainWithTracking:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyPlainAt(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetDeliveryTime(time.Now().Add(5 * time.Minute))
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlainAt:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyHtml(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetHtml(exampleHtml)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendHtml:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyTracking(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText+"Tracking!\n", toUser)
|
||||
m.SetTracking(false)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendTracking:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyTag(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mailgun.NewMessage(fromUser, exampleSubject, exampleText+"Tags Galore!\n", toUser)
|
||||
m.AddTag("FooTag")
|
||||
m.AddTag("BarTag")
|
||||
m.AddTag("BlortTag")
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendTag:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendLegacyMIME(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
m := mailgun.NewMIMEMessage(ioutil.NopCloser(strings.NewReader(exampleMime)), toUser)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendMIME:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestGetStoredMessage(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
id, err := findStoredMessageID(mg) // somehow...
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// First, get our stored message.
|
||||
msg, err := mg.GetStoredMessage(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fields := map[string]string{
|
||||
" From": msg.From,
|
||||
" Sender": msg.Sender,
|
||||
" Subject": msg.Subject,
|
||||
"Attachments": fmt.Sprintf("%d", len(msg.Attachments)),
|
||||
" Headers": fmt.Sprintf("%d", len(msg.MessageHeaders)),
|
||||
}
|
||||
for k, v := range fields {
|
||||
fmt.Printf("%13s: %s\n", k, v)
|
||||
}
|
||||
|
||||
// We're done with it; now delete it.
|
||||
err = mg.DeleteStoredMessage(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to locate the first stored event type, returning the associated stored message key.
|
||||
func findStoredMessageID(mg mailgun.Mailgun) (string, error) {
|
||||
ei := mg.NewEventIterator()
|
||||
err := ei.GetFirstPage(mailgun.GetEventsOptions{})
|
||||
for{
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ei.Events()) == 0 {
|
||||
break
|
||||
}
|
||||
for _, event := range ei.Events() {
|
||||
if event["event"] == "stored" {
|
||||
s := event["storage"].(map[string]interface{})
|
||||
k := s["key"]
|
||||
return k.(string), nil
|
||||
}
|
||||
}
|
||||
err = ei.GetNext()
|
||||
}
|
||||
return "", fmt.Errorf("No stored messages found. Try changing MG_EMAIL_TO to an address that stores messages and try again.")
|
||||
}
|
||||
|
||||
func TestSendMGPlain(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlain:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGPlainWithTracking(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetTracking(true)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlainWithTracking:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGPlainAt(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetDeliveryTime(time.Now().Add(5 * time.Minute))
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendPlainAt:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGHtml(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText, toUser)
|
||||
m.SetHtml(exampleHtml)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendHtml:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGTracking(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText+"Tracking!\n", toUser)
|
||||
m.SetTracking(false)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendTracking:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGTag(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText+"Tags Galore!\n", toUser)
|
||||
m.AddTag("FooTag")
|
||||
m.AddTag("BarTag")
|
||||
m.AddTag("BlortTag")
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendTag:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGMIME(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
m := mg.NewMIMEMessage(ioutil.NopCloser(strings.NewReader(exampleMime)), toUser)
|
||||
msg, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("TestSendMIME:MSG(" + msg + "),ID(" + id + ")")
|
||||
}
|
||||
|
||||
func TestSendMGBatchFailRecipients(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
m := mg.NewMessage(fromUser, exampleSubject, exampleText+"Batch\n")
|
||||
for i := 0; i < mailgun.MaxNumberOfRecipients; i++ {
|
||||
m.AddRecipient("") // We expect this to indicate a failure at the API
|
||||
}
|
||||
err := m.AddRecipientAndVariables(toUser, nil)
|
||||
if err == nil {
|
||||
// If we're here, either the SDK didn't send the message,
|
||||
// OR the API didn't check for empty To: headers.
|
||||
t.Fatal("Expected to fail!!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMGBatchRecipientVariables(t *testing.T) {
|
||||
toUser := reqEnv(t, "MG_EMAIL_TO")
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
m := mg.NewMessage(fromUser, exampleSubject, templateText)
|
||||
err := m.AddRecipientAndVariables(toUser, map[string]interface{}{
|
||||
"name": "Joe Cool Example",
|
||||
"table": 42,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _, err = mg.Send(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
87
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/routes_test.go
generated
vendored
Normal file
87
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/routes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRouteCRUD(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
var countRoutes = func() int {
|
||||
count, _, err := mg.GetRoutes(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
routeCount := countRoutes()
|
||||
|
||||
newRoute, err := mg.CreateRoute(mailgun.Route{
|
||||
Priority: 1,
|
||||
Description: "Sample Route",
|
||||
Expression: "match_recipient(\".*@samples.mailgun.org\")",
|
||||
Actions: []string{
|
||||
"forward(\"http://example.com/messages/\")",
|
||||
"stop()",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if newRoute.ID == "" {
|
||||
t.Fatal("I expected the route created to have an ID associated with it.")
|
||||
}
|
||||
defer func() {
|
||||
err = mg.DeleteRoute(newRoute.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newCount := countRoutes()
|
||||
if newCount != routeCount {
|
||||
t.Fatalf("Expected %d routes defined; got %d", routeCount, newCount)
|
||||
}
|
||||
}()
|
||||
|
||||
newCount := countRoutes()
|
||||
if newCount <= routeCount {
|
||||
t.Fatalf("Expected %d routes defined; got %d", routeCount+1, newCount)
|
||||
}
|
||||
|
||||
theRoute, err := mg.GetRouteByID(newRoute.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ((newRoute.Priority) != (theRoute.Priority)) ||
|
||||
((newRoute.Description) != (theRoute.Description)) ||
|
||||
((newRoute.Expression) != (theRoute.Expression)) ||
|
||||
(len(newRoute.Actions) != len(theRoute.Actions)) ||
|
||||
((newRoute.CreatedAt) != (theRoute.CreatedAt)) ||
|
||||
((newRoute.ID) != (theRoute.ID)) {
|
||||
t.Fatalf("Expected %#v, got %#v", newRoute, theRoute)
|
||||
}
|
||||
for i, action := range newRoute.Actions {
|
||||
if action != theRoute.Actions[i] {
|
||||
t.Fatalf("Expected %#v, got %#v", newRoute, theRoute)
|
||||
}
|
||||
}
|
||||
|
||||
changedRoute, err := mg.UpdateRoute(newRoute.ID, mailgun.Route{
|
||||
Priority: 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if changedRoute.Priority != 2 {
|
||||
t.Fatalf("Expected a priority of 2; got %d", changedRoute.Priority)
|
||||
}
|
||||
if len(changedRoute.Actions) != 2 {
|
||||
t.Fatalf("Expected actions to not be touched; got %d entries now", len(changedRoute.Actions))
|
||||
}
|
||||
}
|
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/spam_complaints_test.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/spam_complaints_test.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetComplaints(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
n, complaints, err := mg.GetComplaints(-1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(complaints) != n {
|
||||
t.Fatalf("Expected %d complaints; got %d", n, len(complaints))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetComplaintFromBazNoComplaint(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
publicApiKey := reqEnv(t, "MG_PUBLIC_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, publicApiKey)
|
||||
_, err := mg.GetSingleComplaint("baz@example.com")
|
||||
if err == nil {
|
||||
t.Fatal("Expected not-found error for missing complaint")
|
||||
}
|
||||
ure, ok := err.(*mailgun.UnexpectedResponseError)
|
||||
if !ok {
|
||||
t.Fatal("Expected UnexpectedResponseError")
|
||||
}
|
||||
if ure.Actual != 404 {
|
||||
t.Fatalf("Expected 404 response code; got %d", ure.Actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDeleteComplaint(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
var check = func(count int) {
|
||||
c, _, err := mg.GetComplaints(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c != count {
|
||||
t.Fatalf("Expected baz@example.com to have %d complaints; got %d", count, c)
|
||||
}
|
||||
}
|
||||
|
||||
check(0)
|
||||
|
||||
err := mg.CreateComplaint("baz@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
check(1)
|
||||
|
||||
err = mg.DeleteComplaint("baz@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
check(0)
|
||||
}
|
40
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/stats_test.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/stats_test.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"os"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func TestGetStats(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
totalCount, stats, err := mg.GetStats(-1, -1, nil, "sent", "opened")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Total Count: %d\n", totalCount)
|
||||
tw := tabwriter.NewWriter(os.Stdout, 2, 8, 2, ' ', tabwriter.AlignRight)
|
||||
fmt.Fprintf(tw, "Id\tEvent\tCreatedAt\tTotalCount\t\n")
|
||||
for _, stat := range stats {
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t\n", stat.Id, stat.Event, stat.CreatedAt, stat.TotalCount)
|
||||
}
|
||||
tw.Flush()
|
||||
}
|
||||
|
||||
func TestDeleteTag(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
err := mg.DeleteTag("newsletter")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/unsubscribes_test.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/unsubscribes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mailgun "github.com/mailgun/mailgun-go"
|
||||
"os"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func TestGetUnsubscribes(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
n, us, err := mg.GetUnsubscribes(mailgun.DefaultLimit, mailgun.DefaultSkip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Received %d out of %d unsubscribe records.\n", len(us), n)
|
||||
if len(us) > 0 {
|
||||
tw := &tabwriter.Writer{}
|
||||
tw.Init(os.Stdout, 2, 8, 2, ' ', 0)
|
||||
fmt.Fprintln(tw, "ID\tAddress\tCreated At\tTag\t")
|
||||
for _, u := range us {
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t\n", u.ID, u.Address, u.CreatedAt, u.Tag)
|
||||
}
|
||||
tw.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUnsubscriptionByAddress(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
email := reqEnv(t, "MG_EMAIL_ADDR")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
n, us, err := mg.GetUnsubscribesByAddress(email)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Received %d out of %d unsubscribe records.\n", len(us), n)
|
||||
if len(us) > 0 {
|
||||
tw := &tabwriter.Writer{}
|
||||
tw.Init(os.Stdout, 2, 8, 2, ' ', 0)
|
||||
fmt.Fprintln(tw, "ID\tAddress\tCreated At\tTag\t")
|
||||
for _, u := range us {
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t\n", u.ID, u.Address, u.CreatedAt, u.Tag)
|
||||
}
|
||||
tw.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDestroyUnsubscription(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
email := reqEnv(t, "MG_EMAIL_ADDR")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
// Create unsubscription record
|
||||
err := mg.Unsubscribe(email, "*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Destroy the unsubscription record
|
||||
err = mg.RemoveUnsubscribe(email)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
66
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/webhooks_test.go
generated
vendored
Normal file
66
Godeps/_workspace/src/github.com/mailgun/mailgun-go/acceptance/webhooks_test.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
// +build acceptance
|
||||
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"github.com/mailgun/mailgun-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWebhookCRUD(t *testing.T) {
|
||||
domain := reqEnv(t, "MG_DOMAIN")
|
||||
apiKey := reqEnv(t, "MG_API_KEY")
|
||||
mg := mailgun.NewMailgun(domain, apiKey, "")
|
||||
|
||||
var countHooks = func() int {
|
||||
hooks, err := mg.GetWebhooks()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return len(hooks)
|
||||
}
|
||||
|
||||
hookCount := countHooks()
|
||||
|
||||
err := mg.CreateWebhook("deliver", "http://www.example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
err = mg.DeleteWebhook("deliver")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newCount := countHooks()
|
||||
if newCount != hookCount {
|
||||
t.Fatalf("Expected %d routes defined; got %d", hookCount, newCount)
|
||||
}
|
||||
}()
|
||||
|
||||
newCount := countHooks()
|
||||
if newCount <= hookCount {
|
||||
t.Fatalf("Expected %d routes defined; got %d", hookCount+1, newCount)
|
||||
}
|
||||
|
||||
theURL, err := mg.GetWebhookByType("deliver")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if theURL != "http://www.example.com" {
|
||||
t.Fatalf("Expected http://www.example.com, got %#v", theURL)
|
||||
}
|
||||
|
||||
err = mg.UpdateWebhook("deliver", "http://api.example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hooks, err := mg.GetWebhooks()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if hooks["deliver"] != "http://api.example.com" {
|
||||
t.Fatalf("Expected http://api.example.com, got %#v", hooks["deliver"])
|
||||
}
|
||||
}
|
122
Godeps/_workspace/src/github.com/mailgun/mailgun-go/bounces.go
generated
vendored
Normal file
122
Godeps/_workspace/src/github.com/mailgun/mailgun-go/bounces.go
generated
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bounce aggregates data relating to undeliverable messages to a specific intended recipient,
|
||||
// identified by Address.
|
||||
// Code provides the SMTP error code causing the bounce,
|
||||
// while Error provides a human readable reason why.
|
||||
// CreatedAt provides the time at which Mailgun detected the bounce.
|
||||
type Bounce struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
code interface{} `json:"code"`
|
||||
Address string `json:"address"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type bounceEnvelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Bounce `json:"items"`
|
||||
}
|
||||
|
||||
type singleBounceEnvelope struct {
|
||||
Bounce Bounce `json:"bounce"`
|
||||
}
|
||||
|
||||
// GetCreatedAt parses the textual, RFC-822 timestamp into a standard Go-compatible
|
||||
// Time structure.
|
||||
func (i Bounce) GetCreatedAt() (t time.Time, err error) {
|
||||
return parseMailgunTime(i.CreatedAt)
|
||||
}
|
||||
|
||||
// GetCode will return the bounce code for the message, regardless if it was
|
||||
// returned as a string or as an integer. This method overcomes a protocol
|
||||
// bug in the Mailgun API.
|
||||
func (b Bounce) GetCode() (int, error) {
|
||||
switch c := b.code.(type) {
|
||||
case int:
|
||||
return c, nil
|
||||
case string:
|
||||
return strconv.Atoi(c)
|
||||
default:
|
||||
return -1, strconv.ErrSyntax
|
||||
}
|
||||
}
|
||||
|
||||
// GetBounces returns a complete set of bounces logged against the sender's domain, if any.
|
||||
// The results include the total number of bounces (regardless of skip or limit settings),
|
||||
// and the slice of bounces specified, if successful.
|
||||
// Note that the length of the slice may be smaller than the total number of bounces.
|
||||
func (m *MailgunImpl) GetBounces(limit, skip int) (int, []Bounce, error) {
|
||||
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint))
|
||||
if limit != -1 {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != -1 {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
var response bounceEnvelope
|
||||
err := getResponseFromJSON(r, &response)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
return response.TotalCount, response.Items, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
var response singleBounceEnvelope
|
||||
err := getResponseFromJSON(r, &response)
|
||||
return response.Bounce, err
|
||||
}
|
||||
|
||||
// AddBounce files a bounce report.
|
||||
// Address identifies the intended recipient of the message that bounced.
|
||||
// Code corresponds to the numeric response given by the e-mail server which rejected the message.
|
||||
// Error providees the corresponding human readable reason for the problem.
|
||||
// For example,
|
||||
// here's how the these two fields relate.
|
||||
// Suppose the SMTP server responds with an error, as below.
|
||||
// Then, . . .
|
||||
//
|
||||
// 550 Requested action not taken: mailbox unavailable
|
||||
// \___/\_______________________________________________/
|
||||
// | |
|
||||
// `-- Code `-- Error
|
||||
//
|
||||
// Note that both code and error exist as strings, even though
|
||||
// code will report as a number.
|
||||
func (m *MailgunImpl) AddBounce(address, code, error string) error {
|
||||
r := simplehttp.NewHTTPRequest(generateApiUrl(m, bouncesEndpoint))
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
payload := simplehttp.NewUrlEncodedPayload()
|
||||
payload.AddValue("address", address)
|
||||
if code != "" {
|
||||
payload.AddValue("code", code)
|
||||
}
|
||||
if error != "" {
|
||||
payload.AddValue("error", error)
|
||||
}
|
||||
_, err := makePostRequest(r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
79
Godeps/_workspace/src/github.com/mailgun/mailgun-go/campaigns.go
generated
vendored
Normal file
79
Godeps/_workspace/src/github.com/mailgun/mailgun-go/campaigns.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
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 {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DeliveredCount int `json:"delivered_count"`
|
||||
ClickedCount int `json:"clicked_count"`
|
||||
OpenedCount int `json:"opened_count"`
|
||||
SubmittedCount int `json:"submitted_count"`
|
||||
UnsubscribedCount int `json:"unsubscribed_count"`
|
||||
BouncedCount int `json:"bounced_count"`
|
||||
ComplainedCount int `json:"complained_count"`
|
||||
DroppedCount int `json:"dropped_count"`
|
||||
}
|
||||
|
||||
type campaignsEnvelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Campaign `json:"items"`
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
var envelope campaignsEnvelope
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
return envelope.TotalCount, envelope.Items, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
payload := simplehttp.NewUrlEncodedPayload()
|
||||
payload.AddValue("name", name)
|
||||
if id != "" {
|
||||
payload.AddValue("id", id)
|
||||
}
|
||||
_, err := makePostRequest(r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
payload := simplehttp.NewUrlEncodedPayload()
|
||||
payload.AddValue("name", name)
|
||||
if newId != "" {
|
||||
payload.AddValue("id", newId)
|
||||
}
|
||||
_, err := makePostRequest(r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
76
Godeps/_workspace/src/github.com/mailgun/mailgun-go/credentials.go
generated
vendored
Normal file
76
Godeps/_workspace/src/github.com/mailgun/mailgun-go/credentials.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Credential structure describes a principle allowed to send or receive mail at the domain.
|
||||
type Credential struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// ErrEmptyParam results occur when a required parameter is missing.
|
||||
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, ""))
|
||||
if limit != DefaultLimit {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
var envelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Credential `json:"items"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
return envelope.TotalCount, envelope.Items, nil
|
||||
}
|
||||
|
||||
// CreateCredential attempts to create associate a new principle with your domain.
|
||||
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)
|
||||
_, err := makePostRequest(r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeCredentialPassword attempts to alter the indicated credential's password.
|
||||
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)
|
||||
_, err := makePutRequest(r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCredential attempts to remove the indicated principle from the domain.
|
||||
func (mg *MailgunImpl) DeleteCredential(id string) error {
|
||||
if id == "" {
|
||||
return ErrEmptyParam
|
||||
}
|
||||
r := simplehttp.NewHTTPRequest(generateCredentialsUrl(mg, id))
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
121
Godeps/_workspace/src/github.com/mailgun/mailgun-go/domains.go
generated
vendored
Normal file
121
Godeps/_workspace/src/github.com/mailgun/mailgun-go/domains.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultLimit and DefaultSkip instruct the SDK to rely on Mailgun's reasonable defaults for pagination settings.
|
||||
const (
|
||||
DefaultLimit = -1
|
||||
DefaultSkip = -1
|
||||
)
|
||||
|
||||
// Disabled, Tag, and Delete indicate spam actions.
|
||||
// Disabled prevents Mailgun from taking any action on what it perceives to be spam.
|
||||
// Tag instruments the received message with headers providing a measure of its spamness.
|
||||
// Delete instructs Mailgun to just block or delete the message all-together.
|
||||
const (
|
||||
Tag = "tag"
|
||||
Disabled = "disabled"
|
||||
Delete = "delete"
|
||||
)
|
||||
|
||||
// A Domain structure holds information about a domain used when sending mail.
|
||||
// The SpamAction field must be one of Tag, Disabled, or Delete.
|
||||
type Domain struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
SMTPLogin string `json:"smtp_login"`
|
||||
Name string `json:"name"`
|
||||
SMTPPassword string `json:"smtp_password"`
|
||||
Wildcard bool `json:"wildcard"`
|
||||
SpamAction string `json:"spam_action"`
|
||||
}
|
||||
|
||||
// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun.
|
||||
// Note that Mailgun does not host DNS records.
|
||||
type DNSRecord struct {
|
||||
Priority string
|
||||
RecordType string `json:"record_type"`
|
||||
Valid string
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type domainsEnvelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Domain `json:"items"`
|
||||
}
|
||||
|
||||
type singleDomainEnvelope struct {
|
||||
Domain Domain `json:"domain"`
|
||||
ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"`
|
||||
SendingDNSRecords []DNSRecord `json:"sending_dns_records"`
|
||||
}
|
||||
|
||||
// GetCreatedAt returns the time the domain was created as a normal Go time.Time type.
|
||||
func (d Domain) GetCreatedAt() (t time.Time, err error) {
|
||||
t, err = parseMailgunTime(d.CreatedAt)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDomains retrieves a set of domains from Mailgun.
|
||||
//
|
||||
// Assuming no error, both the number of items retrieved and a slice of Domain instances.
|
||||
// The number of items returned may be less than the specified limit, if it's specified.
|
||||
// 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))
|
||||
if limit != DefaultLimit {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
var envelope domainsEnvelope
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
return envelope.TotalCount, envelope.Items, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
var envelope singleDomainEnvelope
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
return envelope.Domain, envelope.ReceivingDNSRecords, envelope.SendingDNSRecords, err
|
||||
}
|
||||
|
||||
// CreateDomain instructs Mailgun to create a new domain for your account.
|
||||
// The name parameter identifies the domain.
|
||||
// The smtpPassword parameter provides an access credential for the domain.
|
||||
// The spamAction domain must be one of Delete, Tag, or Disabled.
|
||||
// The wildcard parameter instructs Mailgun to treat all subdomains of this domain uniformly if true,
|
||||
// and as different domains if false.
|
||||
func (m *MailgunImpl) CreateDomain(name string, smtpPassword string, spamAction string, wildcard bool) error {
|
||||
r := simplehttp.NewHTTPRequest(generatePublicApiUrl(domainsEndpoint))
|
||||
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))
|
||||
_, 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/email_validation.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/mailgun/mailgun-go/email_validation.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The EmailVerificationParts structure breaks out the basic elements of an email address.
|
||||
// LocalPart includes everything up to the '@' in an e-mail address.
|
||||
// Domain includes everything after the '@'.
|
||||
// DisplayName is no longer used, and will appear as "".
|
||||
type EmailVerificationParts struct {
|
||||
LocalPart string `json:"local_part"`
|
||||
Domain string `json:"domain"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
// EmailVerification records basic facts about a validated e-mail address.
|
||||
// See the ValidateEmail method and example for more details.
|
||||
//
|
||||
// IsValid indicates whether an email address conforms to IETF RFC standards.
|
||||
// Parts records the different subfields of an email address.
|
||||
// Address echoes the address provided.
|
||||
// DidYouMean provides a simple recommendation in case the address is invalid or
|
||||
// Mailgun thinks you might have a typo.
|
||||
// DidYouMean may be empty (""), in which case Mailgun has no recommendation to give.
|
||||
// The existence of DidYouMean does NOT imply the email provided has anything wrong with it.
|
||||
type EmailVerification struct {
|
||||
IsValid bool `json:"is_valid"`
|
||||
Parts EmailVerificationParts `json:"parts"`
|
||||
Address string `json:"address"`
|
||||
DidYouMean string `json:"did_you_mean"`
|
||||
}
|
||||
|
||||
type addressParseResult struct {
|
||||
Parsed []string `json:"parsed"`
|
||||
Unparseable []string `json:"unparseable"`
|
||||
}
|
||||
|
||||
// ValidateEmail performs various checks on the email address provided to ensure it's correctly formatted.
|
||||
// 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())
|
||||
|
||||
var response EmailVerification
|
||||
err := getResponseFromJSON(r, &response)
|
||||
if err != nil {
|
||||
return EmailVerification{}, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
var response addressParseResult
|
||||
err := getResponseFromJSON(r, &response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.Parsed, response.Unparseable, nil
|
||||
}
|
130
Godeps/_workspace/src/github.com/mailgun/mailgun-go/events.go
generated
vendored
Normal file
130
Godeps/_workspace/src/github.com/mailgun/mailgun-go/events.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Events are open-ended, loosely-defined JSON documents.
|
||||
// They will always have an event and a timestamp field, however.
|
||||
type Event map[string]interface{}
|
||||
|
||||
// noTime always equals an uninitialized Time structure.
|
||||
// It's used to detect when a time parameter is provided.
|
||||
var noTime time.Time
|
||||
|
||||
// GetEventsOptions lets the caller of GetEvents() specify how the results are to be returned.
|
||||
// Begin and End time-box the results returned.
|
||||
// ForceAscending and ForceDescending are used to force Mailgun to use a given traversal order of the events.
|
||||
// If both ForceAscending and ForceDescending are true, an error will result.
|
||||
// If none, the default will be inferred from the Begin and End parameters.
|
||||
// Limit caps the number of results returned. If left unspecified, Mailgun assumes 100.
|
||||
// Compact, if true, compacts the returned JSON to minimize transmission bandwidth.
|
||||
// Otherwise, the JSON is spaced appropriately for human consumption.
|
||||
// Filter allows the caller to provide more specialized filters on the query.
|
||||
// Consult the Mailgun documentation for more details.
|
||||
type GetEventsOptions struct {
|
||||
Begin, End time.Time
|
||||
ForceAscending, ForceDescending, Compact bool
|
||||
Limit int
|
||||
Filter map[string]string
|
||||
}
|
||||
|
||||
// EventIterator maintains the state necessary for paging though small parcels of a larger set of events.
|
||||
type EventIterator struct {
|
||||
events []Event
|
||||
nextURL, prevURL string
|
||||
mg Mailgun
|
||||
}
|
||||
|
||||
// NewEventIterator creates a new iterator for events.
|
||||
// Use GetFirstPage to retrieve the first batch of events.
|
||||
// Use Next and Previous thereafter as appropriate to iterate through sets of data.
|
||||
func (mg *MailgunImpl) NewEventIterator() *EventIterator {
|
||||
return &EventIterator{mg: mg}
|
||||
}
|
||||
|
||||
// Events returns the most recently retrieved batch of events.
|
||||
// The length is guaranteed to fall between 0 and the limit set in the GetEventsOptions structure passed to GetFirstPage.
|
||||
func (ei *EventIterator) Events() []Event {
|
||||
return ei.events
|
||||
}
|
||||
|
||||
// GetFirstPage retrieves the first batch of events, according to your criteria.
|
||||
// See the GetEventsOptions structure for more details on how the fields affect the data returned.
|
||||
func (ei *EventIterator) GetFirstPage(opts GetEventsOptions) error {
|
||||
if opts.ForceAscending && opts.ForceDescending {
|
||||
return fmt.Errorf("collation cannot at once be both ascending and descending")
|
||||
}
|
||||
|
||||
payload := simplehttp.NewUrlEncodedPayload()
|
||||
if opts.Limit != 0 {
|
||||
payload.AddValue("limit", fmt.Sprintf("%d", opts.Limit))
|
||||
}
|
||||
if opts.Compact {
|
||||
payload.AddValue("pretty", "no")
|
||||
}
|
||||
if opts.ForceAscending {
|
||||
payload.AddValue("ascending", "yes")
|
||||
}
|
||||
if opts.ForceDescending {
|
||||
payload.AddValue("ascending", "no")
|
||||
}
|
||||
if opts.Begin != noTime {
|
||||
payload.AddValue("begin", formatMailgunTime(&opts.Begin))
|
||||
}
|
||||
if opts.End != noTime {
|
||||
payload.AddValue("end", formatMailgunTime(&opts.End))
|
||||
}
|
||||
if opts.Filter != nil {
|
||||
for k, v := range opts.Filter {
|
||||
payload.AddValue(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
url, err := generateParameterizedUrl(ei.mg, eventsEndpoint, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ei.fetch(url)
|
||||
}
|
||||
|
||||
// Retrieves the chronologically previous batch of events, if any exist.
|
||||
// You know you're at the end of the list when len(Events())==0.
|
||||
func (ei *EventIterator) GetPrevious() error {
|
||||
return ei.fetch(ei.prevURL)
|
||||
}
|
||||
|
||||
// Retrieves the chronologically next batch of events, if any exist.
|
||||
// You know you're at the end of the list when len(Events())==0.
|
||||
func (ei *EventIterator) GetNext() error {
|
||||
return ei.fetch(ei.nextURL)
|
||||
}
|
||||
|
||||
// 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())
|
||||
var response map[string]interface{}
|
||||
err := getResponseFromJSON(r, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
items := response["items"].([]interface{})
|
||||
ei.events = make([]Event, len(items))
|
||||
for i, item := range items {
|
||||
ei.events[i] = item.(map[string]interface{})
|
||||
}
|
||||
|
||||
pagings := response["paging"].(map[string]interface{})
|
||||
links := make(map[string]string, len(pagings))
|
||||
for key, page := range pagings {
|
||||
links[key] = page.(string)
|
||||
}
|
||||
ei.nextURL = links["next"]
|
||||
ei.prevURL = links["previous"]
|
||||
return err
|
||||
}
|
117
Godeps/_workspace/src/github.com/mailgun/mailgun-go/examples_test.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/mailgun/mailgun-go/examples_test.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleMailgunImpl_ValidateEmail() {
|
||||
mg := NewMailgun("example.com", "", "my_public_api_key")
|
||||
ev, err := mg.ValidateEmail("joe@example.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !ev.IsValid {
|
||||
log.Fatal("Expected valid e-mail address")
|
||||
}
|
||||
log.Printf("Parts local_part=%s domain=%s display_name=%s", ev.Parts.LocalPart, ev.Parts.Domain, ev.Parts.DisplayName)
|
||||
if ev.DidYouMean != "" {
|
||||
log.Printf("The address is syntactically valid, but perhaps has a typo.")
|
||||
log.Printf("Did you mean %s instead?", ev.DidYouMean)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_ParseAddresses() {
|
||||
mg := NewMailgun("example.com", "", "my_public_api_key")
|
||||
addressesThatParsed, unparsableAddresses, err := mg.ParseAddresses("Alice <alice@example.com>", "bob@example.com", "example.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
hittest := map[string]bool{
|
||||
"Alice <alice@example.com>": true,
|
||||
"bob@example.com": true,
|
||||
}
|
||||
for _, a := range addressesThatParsed {
|
||||
if !hittest[a] {
|
||||
log.Fatalf("Expected %s to be parsable", a)
|
||||
}
|
||||
}
|
||||
if len(unparsableAddresses) != 1 {
|
||||
log.Fatalf("Expected 1 address to be unparsable; got %d", len(unparsableAddresses))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_UpdateList() {
|
||||
mg := NewMailgun("example.com", "my_api_key", "")
|
||||
_, err := mg.UpdateList("joe-stat@example.com", List{
|
||||
Name: "Joe Stat",
|
||||
Description: "Joe's status report list",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_Send_constructed() {
|
||||
mg := NewMailgun("example.com", "my_api_key", "")
|
||||
m := NewMessage(
|
||||
"Excited User <me@example.com>",
|
||||
"Hello World",
|
||||
"Testing some Mailgun Awesomeness!",
|
||||
"baz@example.com",
|
||||
"bar@example.com",
|
||||
)
|
||||
m.SetTracking(true)
|
||||
m.SetDeliveryTime(time.Now().Add(24 * time.Hour))
|
||||
m.SetHtml("<html><body><h1>Testing some Mailgun Awesomeness!!</h1></body></html>")
|
||||
_, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Message id=%s", id)
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_Send_mime() {
|
||||
exampleMime := `Content-Type: text/plain; charset="ascii"
|
||||
Subject: Joe's Example Subject
|
||||
From: Joe Example <joe@example.com>
|
||||
To: BARGLEGARF <bargle.garf@example.com>
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Date: Thu, 6 Mar 2014 00:37:52 +0000
|
||||
|
||||
Testing some Mailgun MIME awesomeness!
|
||||
`
|
||||
mg := NewMailgun("example.com", "my_api_key", "")
|
||||
m := NewMIMEMessage(ioutil.NopCloser(strings.NewReader(exampleMime)), "bargle.garf@example.com")
|
||||
_, id, err := mg.Send(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Message id=%s", id)
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_GetRoutes() {
|
||||
mg := NewMailgun("example.com", "my_api_key", "")
|
||||
n, routes, err := mg.GetRoutes(DefaultLimit, DefaultSkip)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if n > len(routes) {
|
||||
log.Printf("More routes exist than has been returned.")
|
||||
}
|
||||
for _, r := range routes {
|
||||
log.Printf("Route pri=%d expr=%s desc=%s", r.Priority, r.Expression, r.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleMailgunImpl_UpdateRoute() {
|
||||
mg := NewMailgun("example.com", "my_api_key", "")
|
||||
_, err := mg.UpdateRoute("route-id-here", Route{
|
||||
Priority: 2,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
291
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailgun.go
generated
vendored
Normal file
291
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailgun.go
generated
vendored
Normal file
|
@ -0,0 +1,291 @@
|
|||
// TODO(sfalvo):
|
||||
// Document how to run acceptance tests.
|
||||
|
||||
// The mailgun package provides methods for interacting with the Mailgun API.
|
||||
// It automates the HTTP request/response cycle, encodings, and other details needed by the API.
|
||||
// This SDK lets you do everything the API lets you, in a more Go-friendly way.
|
||||
//
|
||||
// For further information please see the Mailgun documentation at
|
||||
// http://documentation.mailgun.com/
|
||||
//
|
||||
// Original Author: Michael Banzon
|
||||
// Contributions: Samuel A. Falvo II <sam.falvo %at% rackspace.com>
|
||||
// Version: 0.99.0
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// This document includes a number of examples which illustrates some aspects of the GUI which might be misleading or confusing.
|
||||
// All examples included are derived from an acceptance test.
|
||||
// Note that every SDK function has a corresponding acceptance test, so
|
||||
// if you don't find an example for a function you'd like to know more about,
|
||||
// please check the acceptance sub-package for a corresponding test.
|
||||
// Of course, contributions to the documentation are always welcome as well.
|
||||
// Feel free to submit a pull request or open a Github issue if you cannot find an example to suit your needs.
|
||||
//
|
||||
// Limit and Skip Settings
|
||||
//
|
||||
// Many SDK functions consume a pair of parameters called limit and skip.
|
||||
// These help control how much data Mailgun sends over the wire.
|
||||
// Limit, as you'd expect, gives a count of the number of records you want to receive.
|
||||
// Note that, at present, Mailgun imposes its own cap of 100, for all API endpoints.
|
||||
// Skip indicates where in the data set you want to start receiving from.
|
||||
// Mailgun defaults to the very beginning of the dataset if not specified explicitly.
|
||||
//
|
||||
// If you don't particularly care how much data you receive, you may specify DefaultLimit.
|
||||
// If you similarly don't care about where the data starts, you may specify DefaultSkip.
|
||||
//
|
||||
// Functions that Return Totals
|
||||
//
|
||||
// Functions which accept a limit and skip setting, in general,
|
||||
// will also return a total count of the items returned.
|
||||
// Note that this total count is not the total in the bundle returned by the call.
|
||||
// You can determine that easily enough with Go's len() function.
|
||||
// The total that you receive actually refers to the complete set of data on the server.
|
||||
// This total may well exceed the size returned from the API.
|
||||
//
|
||||
// If this happens, you may find yourself needing to iterate over the dataset of interest.
|
||||
// For example:
|
||||
//
|
||||
// // Get total amount of stuff we have to work with.
|
||||
// mg := NewMailgun("example.com", "my_api_key", "")
|
||||
// n, _, err := mg.GetStats(1, 0, nil, "sent", "opened")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// // Loop over it all.
|
||||
// for sk := 0; sk < n; sk += limit {
|
||||
// _, stats, err := mg.GetStats(limit, sk, nil, "sent", "opened")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// doSomethingWith(stats)
|
||||
// }
|
||||
//
|
||||
// License
|
||||
//
|
||||
// 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 names of Mailgun, Michael Banzon, nor the names of their
|
||||
// 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.
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
apiBase = "https://api.mailgun.net/v2"
|
||||
messagesEndpoint = "messages"
|
||||
mimeMessagesEndpoint = "messages.mime"
|
||||
addressValidateEndpoint = "address/validate"
|
||||
addressParseEndpoint = "address/parse"
|
||||
bouncesEndpoint = "bounces"
|
||||
statsEndpoint = "stats"
|
||||
domainsEndpoint = "domains"
|
||||
deleteTagEndpoint = "tags"
|
||||
campaignsEndpoint = "campaigns"
|
||||
eventsEndpoint = "events"
|
||||
credentialsEndpoint = "credentials"
|
||||
unsubscribesEndpoint = "unsubscribes"
|
||||
routesEndpoint = "routes"
|
||||
webhooksEndpoint = "webhooks"
|
||||
listsEndpoint = "lists"
|
||||
basicAuthUser = "api"
|
||||
)
|
||||
|
||||
// Mailgun defines the supported subset of the Mailgun API.
|
||||
// The Mailgun API may contain additional features which have been deprecated since writing this SDK.
|
||||
// This SDK only covers currently supported interface endpoints.
|
||||
//
|
||||
// Note that Mailgun reserves the right to deprecate endpoints.
|
||||
// Some endpoints listed in this interface may, at any time, become obsolete.
|
||||
// Always double-check with the Mailgun API Documentation to
|
||||
// determine the currently supported feature set.
|
||||
type Mailgun interface {
|
||||
Domain() string
|
||||
ApiKey() string
|
||||
PublicApiKey() string
|
||||
Send(m *Message) (string, string, error)
|
||||
ValidateEmail(email string) (EmailVerification, error)
|
||||
ParseAddresses(addresses ...string) ([]string, []string, error)
|
||||
GetBounces(limit, skip int) (int, []Bounce, error)
|
||||
GetSingleBounce(address string) (Bounce, error)
|
||||
AddBounce(address, code, error string) error
|
||||
DeleteBounce(address string) error
|
||||
GetStats(limit int, skip int, startDate *time.Time, event ...string) (int, []Stat, error)
|
||||
DeleteTag(tag string) error
|
||||
GetDomains(limit, skip int) (int, []Domain, error)
|
||||
GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error)
|
||||
CreateDomain(name string, smtpPassword string, spamAction string, wildcard bool) error
|
||||
DeleteDomain(name string) error
|
||||
GetCampaigns() (int, []Campaign, error)
|
||||
CreateCampaign(name, id string) error
|
||||
UpdateCampaign(oldId, name, newId string) error
|
||||
DeleteCampaign(id string) error
|
||||
GetComplaints(limit, skip int) (int, []Complaint, error)
|
||||
GetSingleComplaint(address string) (Complaint, error)
|
||||
GetStoredMessage(id string) (StoredMessage, error)
|
||||
GetStoredMessageRaw(id string) (StoredMessageRaw, error)
|
||||
DeleteStoredMessage(id string) error
|
||||
GetCredentials(limit, skip int) (int, []Credential, error)
|
||||
CreateCredential(login, password string) error
|
||||
ChangeCredentialPassword(id, password string) error
|
||||
DeleteCredential(id string) error
|
||||
GetUnsubscribes(limit, skip int) (int, []Unsubscription, error)
|
||||
GetUnsubscribesByAddress(string) (int, []Unsubscription, error)
|
||||
Unsubscribe(address, tag string) error
|
||||
RemoveUnsubscribe(string) error
|
||||
CreateComplaint(string) error
|
||||
DeleteComplaint(string) error
|
||||
GetRoutes(limit, skip int) (int, []Route, error)
|
||||
GetRouteByID(string) (Route, error)
|
||||
CreateRoute(Route) (Route, error)
|
||||
DeleteRoute(string) error
|
||||
UpdateRoute(string, Route) (Route, error)
|
||||
GetWebhooks() (map[string]string, error)
|
||||
CreateWebhook(kind, url string) error
|
||||
DeleteWebhook(kind string) error
|
||||
GetWebhookByType(kind string) (string, error)
|
||||
UpdateWebhook(kind, url string) error
|
||||
GetLists(limit, skip int, filter string) (int, []List, error)
|
||||
CreateList(List) (List, error)
|
||||
DeleteList(string) error
|
||||
GetListByAddress(string) (List, error)
|
||||
UpdateList(string, List) (List, error)
|
||||
GetMembers(limit, skip int, subfilter *bool, address string) (int, []Member, error)
|
||||
GetMemberByAddress(MemberAddr, listAddr string) (Member, error)
|
||||
CreateMember(merge bool, addr string, prototype Member) error
|
||||
CreateMemberList(subscribed *bool, addr string, newMembers []interface{}) error
|
||||
UpdateMember(Member, list string, prototype Member) (Member, error)
|
||||
DeleteMember(Member, list string) error
|
||||
NewMessage(from, subject, text string, to ...string) *Message
|
||||
NewMIMEMessage(body io.ReadCloser, to ...string) *Message
|
||||
NewEventIterator() *EventIterator
|
||||
}
|
||||
|
||||
// MailgunImpl bundles data needed by a large number of methods in order to interact with the Mailgun API.
|
||||
// Colloquially, we refer to instances of this structure as "clients."
|
||||
type MailgunImpl struct {
|
||||
domain string
|
||||
apiKey string
|
||||
publicApiKey string
|
||||
}
|
||||
|
||||
// NewMailGun creates a new client instance.
|
||||
func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
|
||||
m := MailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey}
|
||||
return &m
|
||||
}
|
||||
|
||||
// Domain returns the domain configured for this client.
|
||||
func (m *MailgunImpl) Domain() string {
|
||||
return m.domain
|
||||
}
|
||||
|
||||
// ApiKey returns the API key configured for this client.
|
||||
func (m *MailgunImpl) ApiKey() string {
|
||||
return m.apiKey
|
||||
}
|
||||
|
||||
// PublicApiKey returns the public API key configured for this client.
|
||||
func (m *MailgunImpl) PublicApiKey() string {
|
||||
return m.publicApiKey
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// generateMemberApiUrl renders a URL relevant for specifying mailing list members.
|
||||
// The address parameter refers to the mailing list in question.
|
||||
func generateMemberApiUrl(endpoint, address string) string {
|
||||
return fmt.Sprintf("%s/%s/%s/members", apiBase, endpoint, address)
|
||||
}
|
||||
|
||||
// generateApiUrlWithTarget works as generateApiUrl,
|
||||
// but consumes an additional resource parameter called 'target'.
|
||||
func generateApiUrlWithTarget(m Mailgun, endpoint, target string) string {
|
||||
tail := ""
|
||||
if target != "" {
|
||||
tail = fmt.Sprintf("/%s", target)
|
||||
}
|
||||
return fmt.Sprintf("%s%s", generateApiUrl(m, endpoint), tail)
|
||||
}
|
||||
|
||||
// generateDomainApiUrl renders a URL as generateApiUrl, but
|
||||
// addresses a family of functions which have a non-standard URL structure.
|
||||
// Most URLs consume a domain in the 2nd position, but some endpoints
|
||||
// require the word "domains" to be there instead.
|
||||
func generateDomainApiUrl(m Mailgun, endpoint string) string {
|
||||
return fmt.Sprintf("%s/domains/%s/%s", apiBase, m.Domain(), endpoint)
|
||||
}
|
||||
|
||||
// generateCredentialsUrl renders a URL as generateDomainApiUrl,
|
||||
// but focuses on the SMTP credentials family of API functions.
|
||||
func generateCredentialsUrl(m Mailgun, id string) string {
|
||||
tail := ""
|
||||
if id != "" {
|
||||
tail = fmt.Sprintf("/%s", id)
|
||||
}
|
||||
return generateDomainApiUrl(m, fmt.Sprintf("credentials%s", tail))
|
||||
// return fmt.Sprintf("%s/domains/%s/credentials%s", apiBase, m.Domain(), tail)
|
||||
}
|
||||
|
||||
// generateStoredMessageUrl generates the URL needed to acquire a copy of a stored message.
|
||||
func generateStoredMessageUrl(m Mailgun, endpoint, id string) string {
|
||||
return generateDomainApiUrl(m, fmt.Sprintf("%s/%s", endpoint, id))
|
||||
// return fmt.Sprintf("%s/domains/%s/%s/%s", apiBase, m.Domain(), endpoint, id)
|
||||
}
|
||||
|
||||
// generatePublicApiUrl works as generateApiUrl, except that generatePublicApiUrl has no need for the domain.
|
||||
func generatePublicApiUrl(endpoint string) string {
|
||||
return fmt.Sprintf("%s/%s", apiBase, endpoint)
|
||||
}
|
||||
|
||||
// generateParameterizedUrl works as generateApiUrl, but supports query parameters.
|
||||
func generateParameterizedUrl(m Mailgun, endpoint string, payload simplehttp.Payload) (string, error) {
|
||||
paramBuffer, err := payload.GetPayloadBuffer()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := string(paramBuffer.Bytes())
|
||||
return fmt.Sprintf("%s?%s", generateApiUrl(m, eventsEndpoint), params), nil
|
||||
}
|
||||
|
||||
// parseMailgunTime translates a timestamp as returned by Mailgun into a Go standard timestamp.
|
||||
func parseMailgunTime(ts string) (t time.Time, err error) {
|
||||
t, err = time.Parse("Mon, 2 Jan 2006 15:04:05 MST", ts)
|
||||
return
|
||||
}
|
||||
|
||||
// formatMailgunTime translates a timestamp into a human-readable form.
|
||||
func formatMailgunTime(t *time.Time) string {
|
||||
return t.Format("Mon, 2 Jan 2006 15:04:05 -0700")
|
||||
}
|
68
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailgun_test.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailgun_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const domain = "valid-mailgun-domain"
|
||||
const apiKey = "valid-mailgun-api-key"
|
||||
const publicApiKey = "valid-mailgun-public-api-key"
|
||||
|
||||
func TestMailgun(t *testing.T) {
|
||||
m := NewMailgun(domain, apiKey, publicApiKey)
|
||||
|
||||
if domain != m.Domain() {
|
||||
t.Fatal("Domain not equal!")
|
||||
}
|
||||
|
||||
if apiKey != m.ApiKey() {
|
||||
t.Fatal("ApiKey not equal!")
|
||||
}
|
||||
|
||||
if publicApiKey != m.PublicApiKey() {
|
||||
t.Fatal("PublicApiKey not equal!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBounceGetCode(t *testing.T) {
|
||||
b1 := &Bounce{
|
||||
CreatedAt: "blah",
|
||||
code: 123,
|
||||
Address: "blort",
|
||||
Error: "bletch",
|
||||
}
|
||||
c, err := b1.GetCode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c != 123 {
|
||||
t.Fatal("Expected 123; got ", c)
|
||||
}
|
||||
|
||||
b2 := &Bounce{
|
||||
CreatedAt: "blah",
|
||||
code: "456",
|
||||
Address: "blort",
|
||||
Error: "Bletch",
|
||||
}
|
||||
c, err = b2.GetCode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c != 456 {
|
||||
t.Fatal("Expected 456; got ", c)
|
||||
}
|
||||
|
||||
b3 := &Bounce{
|
||||
CreatedAt: "blah",
|
||||
code: "456H",
|
||||
Address: "blort",
|
||||
Error: "Bletch",
|
||||
}
|
||||
c, err = b3.GetCode()
|
||||
e, ok := err.(*strconv.NumError)
|
||||
if !ok && e != nil {
|
||||
t.Fatal("Expected a syntax error in numeric conversion: got ", err)
|
||||
}
|
||||
}
|
307
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailing_lists.go
generated
vendored
Normal file
307
Godeps/_workspace/src/github.com/mailgun/mailgun-go/mailing_lists.go
generated
vendored
Normal file
|
@ -0,0 +1,307 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A mailing list may have one of three membership modes.
|
||||
// ReadOnly specifies that nobody, including Members,
|
||||
// may send messages to the mailing list.
|
||||
// Messages distributed on such lists come from list administrator accounts only.
|
||||
// Members specifies that only those who subscribe to the mailing list may send messages.
|
||||
// Everyone specifies that anyone and everyone may both read and submit messages
|
||||
// to the mailing list, including non-subscribers.
|
||||
const (
|
||||
ReadOnly = "readonly"
|
||||
Members = "members"
|
||||
Everyone = "everyone"
|
||||
)
|
||||
|
||||
// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
|
||||
// This attribute may be used to filter the results returned by GetSubscribers().
|
||||
// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
|
||||
var (
|
||||
All *bool = nil
|
||||
Subscribed *bool = &yes
|
||||
Unsubscribed *bool = &no
|
||||
)
|
||||
|
||||
// yes and no are variables which provide us the ability to take their addresses.
|
||||
// Subscribed and Unsubscribed are pointers to these booleans.
|
||||
//
|
||||
// We use a pointer to boolean as a kind of trinary data type:
|
||||
// if nil, the relevant data type remains unspecified.
|
||||
// Otherwise, its value is either true or false.
|
||||
var (
|
||||
yes bool = true
|
||||
no bool = false
|
||||
)
|
||||
|
||||
// A List structure provides information for a mailing list.
|
||||
//
|
||||
// AccessLevel may be one of ReadOnly, Members, or Everyone.
|
||||
type List struct {
|
||||
Address string `json:"address",omitempty"`
|
||||
Name string `json:"name",omitempty"`
|
||||
Description string `json:"description",omitempty"`
|
||||
AccessLevel string `json:"access_level",omitempty"`
|
||||
CreatedAt string `json:"created_at",omitempty"`
|
||||
MembersCount int `json:"members_count",omitempty"`
|
||||
}
|
||||
|
||||
// A Member structure represents a member of the mailing list.
|
||||
// The Vars field can represent any JSON-encodable data.
|
||||
type Member struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Subscribed *bool `json:"subscribed,omitempty"`
|
||||
Vars map[string]interface{} `json:"vars,omitempty"`
|
||||
}
|
||||
|
||||
// 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()
|
||||
if limit != DefaultLimit {
|
||||
p.AddValue("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
p.AddValue("skip", strconv.Itoa(skip))
|
||||
}
|
||||
if filter != "" {
|
||||
p.AddValue("address", filter)
|
||||
}
|
||||
var envelope struct {
|
||||
Items []List `json:"items"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
response, err := makeRequest(r, "GET", p)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
err = response.ParseFromJSON(&envelope)
|
||||
return envelope.TotalCount, envelope.Items, err
|
||||
}
|
||||
|
||||
// CreateList creates a new mailing list under your Mailgun account.
|
||||
// You need specify only the Address and Name members of the prototype;
|
||||
// Description, and AccessLevel are optional.
|
||||
// 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()
|
||||
if prototype.Address != "" {
|
||||
p.AddValue("address", prototype.Address)
|
||||
}
|
||||
if prototype.Name != "" {
|
||||
p.AddValue("name", prototype.Name)
|
||||
}
|
||||
if prototype.Description != "" {
|
||||
p.AddValue("description", prototype.Description)
|
||||
}
|
||||
if 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)
|
||||
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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
response, err := makeGetRequest(r)
|
||||
var envelope struct {
|
||||
List `json:"list"`
|
||||
}
|
||||
err = response.ParseFromJSON(&envelope)
|
||||
return envelope.List, err
|
||||
}
|
||||
|
||||
// UpdateList allows you to change various attributes of a list.
|
||||
// Address, Name, Description, and AccessLevel are all optional;
|
||||
// only those fields which are set in the prototype will change.
|
||||
//
|
||||
// Be careful! If changing the address of a mailing list,
|
||||
// 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()
|
||||
if prototype.Address != "" {
|
||||
p.AddValue("address", prototype.Address)
|
||||
}
|
||||
if prototype.Name != "" {
|
||||
p.AddValue("name", prototype.Name)
|
||||
}
|
||||
if prototype.Description != "" {
|
||||
p.AddValue("description", prototype.Description)
|
||||
}
|
||||
if 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)
|
||||
return l, err
|
||||
}
|
||||
|
||||
// GetMembers returns the list of members belonging to the indicated mailing list.
|
||||
// The s parameter can be set to one of three settings to help narrow the returned data set:
|
||||
// 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()
|
||||
if limit != DefaultLimit {
|
||||
p.AddValue("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
p.AddValue("skip", strconv.Itoa(skip))
|
||||
}
|
||||
if s != nil {
|
||||
p.AddValue("subscribed", yesNo(*s))
|
||||
}
|
||||
var envelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Member `json:"items"`
|
||||
}
|
||||
response, err := makeRequest(r, "GET", p)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
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())
|
||||
response, err := makeGetRequest(r)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
var envelope struct {
|
||||
Member Member `json:"member"`
|
||||
}
|
||||
err = response.ParseFromJSON(&envelope)
|
||||
return envelope.Member, err
|
||||
}
|
||||
|
||||
// CreateMember registers a new member of the indicated mailing list.
|
||||
// If merge is set to true, then the registration may update an existing Member's settings.
|
||||
// Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address.
|
||||
func (mg *MailgunImpl) CreateMember(merge bool, addr string, prototype Member) error {
|
||||
vs, err := json.Marshal(prototype.Vars)
|
||||
if err != nil {
|
||||
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))
|
||||
if prototype.Subscribed != nil {
|
||||
p.AddValue("subscribed", yesNo(*prototype.Subscribed))
|
||||
}
|
||||
_, err = makePostRequest(r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateMember lets you change certain details about the indicated mailing list member.
|
||||
// Address, Name, Vars, and Subscribed fields may be changed.
|
||||
func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, error) {
|
||||
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, l) + "/" + s)
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
p := simplehttp.NewFormDataPayload()
|
||||
if prototype.Address != "" {
|
||||
p.AddValue("address", prototype.Address)
|
||||
}
|
||||
if prototype.Name != "" {
|
||||
p.AddValue("name", prototype.Name)
|
||||
}
|
||||
if prototype.Vars != nil {
|
||||
vs, err := json.Marshal(prototype.Vars)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
p.AddValue("vars", string(vs))
|
||||
}
|
||||
if prototype.Subscribed != nil {
|
||||
p.AddValue("subscribed", yesNo(*prototype.Subscribed))
|
||||
}
|
||||
response, err := makePutRequest(r, p)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
var envelope struct {
|
||||
Member Member `json:"member"`
|
||||
}
|
||||
err = response.ParseFromJSON(&envelope)
|
||||
return envelope.Member, err
|
||||
}
|
||||
|
||||
// DeleteMember removes the member from the list.
|
||||
func (mg *MailgunImpl) DeleteMember(member, addr string) error {
|
||||
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + "/" + member)
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateMemberList registers multiple Members and non-Member members to a single mailing list
|
||||
// in a single round-trip.
|
||||
// s indicates the default subscribed status (Subscribed or Unsubscribed).
|
||||
// Use All to elect not to provide a default.
|
||||
// The newMembers list can take one of two JSON-encodable forms: an slice of strings, or
|
||||
// a slice of Member structures.
|
||||
// If a simple slice of strings is passed, each string refers to the member's e-mail address.
|
||||
// Otherwise, each Member needs to have at least the Address field filled out.
|
||||
// Other fields are optional, but may be set according to your needs.
|
||||
func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error {
|
||||
r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
p := simplehttp.NewFormDataPayload()
|
||||
if s != nil {
|
||||
p.AddValue("subscribed", yesNo(*s))
|
||||
}
|
||||
bs, err := json.Marshal(newMembers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(bs))
|
||||
p.AddValue("members", string(bs))
|
||||
_, err = makePostRequest(r, p)
|
||||
return err
|
||||
}
|
647
Godeps/_workspace/src/github.com/mailgun/mailgun-go/messages.go
generated
vendored
Normal file
647
Godeps/_workspace/src/github.com/mailgun/mailgun-go/messages.go
generated
vendored
Normal file
|
@ -0,0 +1,647 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/mbanzon/simplehttp"
|
||||
)
|
||||
|
||||
// MaxNumberOfRecipients represents the largest batch of recipients that Mailgun can support in a single API call.
|
||||
// This figure includes To:, Cc:, Bcc:, etc. recipients.
|
||||
const MaxNumberOfRecipients = 1000
|
||||
|
||||
// Message structures contain both the message text and the envelop for an e-mail message.
|
||||
type Message struct {
|
||||
to []string
|
||||
tags []string
|
||||
campaigns []string
|
||||
dkim bool
|
||||
deliveryTime *time.Time
|
||||
attachments []string
|
||||
readerAttachments []ReaderAttachment
|
||||
inlines []string
|
||||
|
||||
testMode bool
|
||||
tracking bool
|
||||
trackingClicks bool
|
||||
trackingOpens bool
|
||||
headers map[string]string
|
||||
variables map[string]string
|
||||
recipientVariables map[string]map[string]interface{}
|
||||
|
||||
dkimSet bool
|
||||
trackingSet bool
|
||||
trackingClicksSet bool
|
||||
trackingOpensSet bool
|
||||
|
||||
specific features
|
||||
mg Mailgun
|
||||
}
|
||||
|
||||
type ReaderAttachment struct {
|
||||
Filename string
|
||||
ReadCloser io.ReadCloser
|
||||
}
|
||||
|
||||
// StoredMessage structures contain the (parsed) message content for an email
|
||||
// sent to a Mailgun account.
|
||||
//
|
||||
// The MessageHeaders field is special, in that it's formatted as a slice of pairs.
|
||||
// Each pair consists of a name [0] and value [1]. Array notation is used instead of a map
|
||||
// because that's how it's sent over the wire, and it's how encoding/json expects this field
|
||||
// to be.
|
||||
type StoredMessage struct {
|
||||
Recipients string `json:"recipients"`
|
||||
Sender string `json:"sender"`
|
||||
From string `json:"from"`
|
||||
Subject string `json:"subject"`
|
||||
BodyPlain string `json:"body-plain"`
|
||||
StrippedText string `json:"stripped-text"`
|
||||
StrippedSignature string `json:"stripped-signature"`
|
||||
BodyHtml string `json:"body-html"`
|
||||
StrippedHtml string `json:"stripped-html"`
|
||||
Attachments []StoredAttachment `json:"attachments"`
|
||||
MessageUrl string `json:"message-url"`
|
||||
ContentIDMap map[string]interface{} `json:"content-id-map"`
|
||||
MessageHeaders [][]string `json:"message-headers"`
|
||||
}
|
||||
|
||||
// StoredAttachment structures contain information on an attachment associated with a stored message.
|
||||
type StoredAttachment struct {
|
||||
Size int `json:"size"`
|
||||
Url string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
ContentType string `json:"content-type"`
|
||||
}
|
||||
|
||||
type StoredMessageRaw struct {
|
||||
Recipients string `json:"recipients"`
|
||||
Sender string `json:"sender"`
|
||||
From string `json:"from"`
|
||||
Subject string `json:"subject"`
|
||||
BodyMime string `json:"body-mime"`
|
||||
}
|
||||
|
||||
// plainMessage contains fields relevant to plain API-synthesized messages.
|
||||
// You're expected to use various setters to set most of these attributes,
|
||||
// although from, subject, and text are set when the message is created with
|
||||
// NewMessage.
|
||||
type plainMessage struct {
|
||||
from string
|
||||
cc []string
|
||||
bcc []string
|
||||
subject string
|
||||
text string
|
||||
html string
|
||||
}
|
||||
|
||||
// mimeMessage contains fields relevant to pre-packaged MIME messages.
|
||||
type mimeMessage struct {
|
||||
body io.ReadCloser
|
||||
}
|
||||
|
||||
type sendMessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// features abstracts the common characteristics between regular and MIME messages.
|
||||
// addCC, addBCC, recipientCount, and setHTML are invoked via the package-global AddCC, AddBCC,
|
||||
// RecipientCount, and SetHtml calls, as these functions are ignored for MIME messages.
|
||||
// Send() invokes addValues to add message-type-specific MIME headers for the API call
|
||||
// to Mailgun. isValid yeilds true if and only if the message is valid enough for sending
|
||||
// through the API. Finally, endpoint() tells Send() which endpoint to use to submit the API call.
|
||||
type features interface {
|
||||
addCC(string)
|
||||
addBCC(string)
|
||||
setHtml(string)
|
||||
addValues(*simplehttp.FormDataPayload)
|
||||
isValid() bool
|
||||
endpoint() string
|
||||
recipientCount() int
|
||||
}
|
||||
|
||||
// NewMessage returns a new e-mail message with the simplest envelop needed to send.
|
||||
//
|
||||
// DEPRECATED.
|
||||
// The package will panic if you use AddRecipient(), AddBcc(), AddCc(), et. al.
|
||||
// on a message already equipped with MaxNumberOfRecipients recipients.
|
||||
// Use Mailgun.NewMessage() instead.
|
||||
// It works similarly to this function, but supports larger lists of recipients.
|
||||
func NewMessage(from string, subject string, text string, to ...string) *Message {
|
||||
return &Message{
|
||||
specific: &plainMessage{
|
||||
from: from,
|
||||
subject: subject,
|
||||
text: text,
|
||||
},
|
||||
to: to,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessage returns a new e-mail message with the simplest envelop needed to send.
|
||||
//
|
||||
// Unlike the global function,
|
||||
// this method supports arbitrary-sized recipient lists by
|
||||
// automatically sending mail in batches of up to MaxNumberOfRecipients.
|
||||
//
|
||||
// To support batch sending, you don't want to provide a fixed To: header at this point.
|
||||
// Pass nil as the to parameter to skip adding the To: header at this stage.
|
||||
// You can do this explicitly, or implicitly, as follows:
|
||||
//
|
||||
// // Note absence of To parameter(s)!
|
||||
// m := mg.NewMessage("me@example.com", "Help save our planet", "Hello world!")
|
||||
//
|
||||
// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method
|
||||
// before sending, though.
|
||||
func (mg *MailgunImpl) NewMessage(from, subject, text string, to ...string) *Message {
|
||||
return &Message{
|
||||
specific: &plainMessage{
|
||||
from: from,
|
||||
subject: subject,
|
||||
text: text,
|
||||
},
|
||||
to: to,
|
||||
mg: mg,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMIMEMessage creates a new MIME message. These messages are largely canned;
|
||||
// you do not need to invoke setters to set message-related headers.
|
||||
// However, you do still need to call setters for Mailgun-specific settings.
|
||||
//
|
||||
// DEPRECATED.
|
||||
// The package will panic if you use AddRecipient(), AddBcc(), AddCc(), et. al.
|
||||
// on a message already equipped with MaxNumberOfRecipients recipients.
|
||||
// Use Mailgun.NewMIMEMessage() instead.
|
||||
// It works similarly to this function, but supports larger lists of recipients.
|
||||
func NewMIMEMessage(body io.ReadCloser, to ...string) *Message {
|
||||
return &Message{
|
||||
specific: &mimeMessage{
|
||||
body: body,
|
||||
},
|
||||
to: to,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMIMEMessage creates a new MIME message. These messages are largely canned;
|
||||
// you do not need to invoke setters to set message-related headers.
|
||||
// However, you do still need to call setters for Mailgun-specific settings.
|
||||
//
|
||||
// Unlike the global function,
|
||||
// this method supports arbitrary-sized recipient lists by
|
||||
// automatically sending mail in batches of up to MaxNumberOfRecipients.
|
||||
//
|
||||
// To support batch sending, you don't want to provide a fixed To: header at this point.
|
||||
// Pass nil as the to parameter to skip adding the To: header at this stage.
|
||||
// You can do this explicitly, or implicitly, as follows:
|
||||
//
|
||||
// // Note absence of To parameter(s)!
|
||||
// m := mg.NewMessage("me@example.com", "Help save our planet", "Hello world!")
|
||||
//
|
||||
// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method
|
||||
// before sending, though.
|
||||
func (mg *MailgunImpl) NewMIMEMessage(body io.ReadCloser, to ...string) *Message {
|
||||
return &Message{
|
||||
specific: &mimeMessage{
|
||||
body: body,
|
||||
},
|
||||
to: to,
|
||||
mg: mg,
|
||||
}
|
||||
}
|
||||
|
||||
// AddReaderAttachment arranges to send a file along with the e-mail message.
|
||||
// File contents are read from a io.ReadCloser.
|
||||
// The filename parameter is the resulting filename of the attachment.
|
||||
// The readCloser parameter is the io.ReadCloser which reads the actual bytes to be used
|
||||
// as the contents of the attached file.
|
||||
func (m *Message) AddReaderAttachment(filename string, readCloser io.ReadCloser) {
|
||||
ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser}
|
||||
m.readerAttachments = append(m.readerAttachments, ra)
|
||||
}
|
||||
|
||||
// AddAttachment arranges to send a file from the filesystem along with the e-mail message.
|
||||
// The attachment parameter is a filename, which must refer to a file which actually resides
|
||||
// in the local filesystem.
|
||||
func (m *Message) AddAttachment(attachment string) {
|
||||
m.attachments = append(m.attachments, attachment)
|
||||
}
|
||||
|
||||
// AddInline arranges to send a file along with the e-mail message, but does so
|
||||
// in a way that its data remains "inline" with the rest of the message. This
|
||||
// can be used to send image or font data along with an HTML-encoded message body.
|
||||
// The attachment parameter is a filename, which must refer to a file which actually resides
|
||||
// in the local filesystem.
|
||||
func (m *Message) AddInline(inline string) {
|
||||
m.inlines = append(m.inlines, inline)
|
||||
}
|
||||
|
||||
// AddRecipient appends a receiver to the To: header of a message.
|
||||
//
|
||||
// NOTE: Above a certain limit (currently 1000 recipients),
|
||||
// this function will cause the message as it's currently defined to be sent.
|
||||
// This allows you to support large mailing lists without running into Mailgun's API limitations.
|
||||
func (m *Message) AddRecipient(recipient string) error {
|
||||
return m.AddRecipientAndVariables(recipient, nil)
|
||||
}
|
||||
|
||||
// AddRecipientAndVariables appends a receiver to the To: header of a message,
|
||||
// and as well attaches a set of variables relevant for this recipient.
|
||||
//
|
||||
// NOTE: Above a certain limit (see MaxNumberOfRecipients),
|
||||
// this function will cause the message as it's currently defined to be sent.
|
||||
// This allows you to support large mailing lists without running into Mailgun's API limitations.
|
||||
func (m *Message) AddRecipientAndVariables(r string, vars map[string]interface{}) error {
|
||||
if m.RecipientCount() >= MaxNumberOfRecipients {
|
||||
_, _, err := m.send()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.to = make([]string, len(m.to))
|
||||
m.recipientVariables = make(map[string]map[string]interface{}, len(m.recipientVariables))
|
||||
}
|
||||
m.to = append(m.to, r)
|
||||
if vars != nil {
|
||||
if m.recipientVariables == nil {
|
||||
m.recipientVariables = make(map[string]map[string]interface{})
|
||||
}
|
||||
m.recipientVariables[r] = vars
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecipientCount returns the total number of recipients for the message.
|
||||
// This includes To:, Cc:, and Bcc: fields.
|
||||
//
|
||||
// NOTE: At present, this method is reliable only for non-MIME messages, as the
|
||||
// Bcc: and Cc: fields are easily accessible.
|
||||
// For MIME messages, only the To: field is considered.
|
||||
// A fix for this issue is planned for a future release.
|
||||
// For now, MIME messages are always assumed to have 10 recipients between Cc: and Bcc: fields.
|
||||
// If your MIME messages have more than 10 non-To: field recipients,
|
||||
// you may find that some recipients will not receive your e-mail.
|
||||
// It's perfectly OK, of course, for a MIME message to not have any Cc: or Bcc: recipients.
|
||||
func (m *Message) RecipientCount() int {
|
||||
return len(m.to) + m.specific.recipientCount()
|
||||
}
|
||||
|
||||
func (pm *plainMessage) recipientCount() int {
|
||||
return len(pm.bcc) + len(pm.cc)
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) recipientCount() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (m *Message) send() (string, string, error) {
|
||||
return m.mg.Send(m)
|
||||
}
|
||||
|
||||
// AddCC appends a receiver to the carbon-copy header of a message.
|
||||
func (m *Message) AddCC(recipient string) {
|
||||
m.specific.addCC(recipient)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) addCC(r string) {
|
||||
pm.cc = append(pm.cc, r)
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) addCC(_ string) {}
|
||||
|
||||
// AddBCC appends a receiver to the blind-carbon-copy header of a message.
|
||||
func (m *Message) AddBCC(recipient string) {
|
||||
m.specific.addBCC(recipient)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) addBCC(r string) {
|
||||
pm.bcc = append(pm.bcc, r)
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) addBCC(_ string) {}
|
||||
|
||||
// If you're sending a message that isn't already MIME encoded, SetHtml() will arrange to bundle
|
||||
// an HTML representation of your message in addition to your plain-text body.
|
||||
func (m *Message) SetHtml(html string) {
|
||||
m.specific.setHtml(html)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) setHtml(h string) {
|
||||
pm.html = h
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) setHtml(_ string) {}
|
||||
|
||||
// AddTag attaches a tag to the message. Tags are useful for metrics gathering and event tracking purposes.
|
||||
// Refer to the Mailgun documentation for further details.
|
||||
func (m *Message) AddTag(tag string) {
|
||||
m.tags = append(m.tags, tag)
|
||||
}
|
||||
|
||||
// This feature is deprecated for new software.
|
||||
func (m *Message) AddCampaign(campaign string) {
|
||||
m.campaigns = append(m.campaigns, campaign)
|
||||
}
|
||||
|
||||
// SetDKIM arranges to send the o:dkim header with the message, and sets its value accordingly.
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) SetDKIM(dkim bool) {
|
||||
m.dkim = dkim
|
||||
m.dkimSet = true
|
||||
}
|
||||
|
||||
// EnableTestMode allows submittal of a message, such that it will be discarded by Mailgun.
|
||||
// This facilitates testing client-side software without actually consuming e-mail resources.
|
||||
func (m *Message) EnableTestMode() {
|
||||
m.testMode = true
|
||||
}
|
||||
|
||||
// SetDeliveryTime schedules the message for transmission at the indicated time.
|
||||
// Pass nil to remove any installed schedule.
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) SetDeliveryTime(dt time.Time) {
|
||||
pdt := new(time.Time)
|
||||
*pdt = dt
|
||||
m.deliveryTime = pdt
|
||||
}
|
||||
|
||||
// SetTracking sets the o:tracking message parameter to adjust, on a message-by-message basis,
|
||||
// whether or not Mailgun will rewrite URLs to facilitate event tracking.
|
||||
// Events tracked includes opens, clicks, unsubscribes, etc.
|
||||
// Note: simply calling this method ensures that the o:tracking header is passed in with the message.
|
||||
// Its yes/no setting is determined by the call's parameter.
|
||||
// Note that this header is not passed on to the final recipient(s).
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) SetTracking(tracking bool) {
|
||||
m.tracking = tracking
|
||||
m.trackingSet = true
|
||||
}
|
||||
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) SetTrackingClicks(trackingClicks bool) {
|
||||
m.trackingClicks = trackingClicks
|
||||
m.trackingClicksSet = true
|
||||
}
|
||||
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) SetTrackingOpens(trackingOpens bool) {
|
||||
m.trackingOpens = trackingOpens
|
||||
m.trackingOpensSet = true
|
||||
}
|
||||
|
||||
// AddHeader allows you to send custom MIME headers with the message.
|
||||
func (m *Message) AddHeader(header, value string) {
|
||||
if m.headers == nil {
|
||||
m.headers = make(map[string]string)
|
||||
}
|
||||
m.headers[header] = value
|
||||
}
|
||||
|
||||
// AddVariable lets you associate a set of variables with messages you send,
|
||||
// which Mailgun can use to, in essence, complete form-mail.
|
||||
// Refer to the Mailgun documentation for more information.
|
||||
func (m *Message) AddVariable(variable string, value interface{}) error {
|
||||
j, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.variables == nil {
|
||||
m.variables = make(map[string]string)
|
||||
}
|
||||
m.variables[variable] = string(j)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send attempts to queue a message (see Message, NewMessage, and its methods) for delivery.
|
||||
// It returns the Mailgun server response, which consists of two components:
|
||||
// a human-readable status message, and a message ID. The status and message ID are set only
|
||||
// if no error occurred.
|
||||
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()
|
||||
|
||||
message.specific.addValues(payload)
|
||||
for _, to := range message.to {
|
||||
payload.AddValue("to", to)
|
||||
}
|
||||
for _, tag := range message.tags {
|
||||
payload.AddValue("o:tag", tag)
|
||||
}
|
||||
for _, campaign := range message.campaigns {
|
||||
payload.AddValue("o:campaign", campaign)
|
||||
}
|
||||
if message.dkimSet {
|
||||
payload.AddValue("o:dkim", yesNo(message.dkim))
|
||||
}
|
||||
if message.deliveryTime != nil {
|
||||
payload.AddValue("o:deliverytime", formatMailgunTime(message.deliveryTime))
|
||||
}
|
||||
if message.testMode {
|
||||
payload.AddValue("o:testmode", "yes")
|
||||
}
|
||||
if message.trackingSet {
|
||||
payload.AddValue("o:tracking", yesNo(message.tracking))
|
||||
}
|
||||
if message.trackingClicksSet {
|
||||
payload.AddValue("o:tracking-clicks", yesNo(message.trackingClicks))
|
||||
}
|
||||
if message.trackingOpensSet {
|
||||
payload.AddValue("o:tracking-opens", yesNo(message.trackingOpens))
|
||||
}
|
||||
if message.headers != nil {
|
||||
for header, value := range message.headers {
|
||||
payload.AddValue("h:"+header, value)
|
||||
}
|
||||
}
|
||||
if message.variables != nil {
|
||||
for variable, value := range message.variables {
|
||||
payload.AddValue("v:"+variable, value)
|
||||
}
|
||||
}
|
||||
if message.recipientVariables != nil {
|
||||
j, err := json.Marshal(message.recipientVariables)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
payload.AddValue("recipient-variables", string(j))
|
||||
}
|
||||
if message.attachments != nil {
|
||||
for _, attachment := range message.attachments {
|
||||
payload.AddFile("attachment", attachment)
|
||||
}
|
||||
}
|
||||
if message.readerAttachments != nil {
|
||||
for _, readerAttachment := range message.readerAttachments {
|
||||
payload.AddReadCloser("attachment", readerAttachment.Filename, readerAttachment.ReadCloser)
|
||||
}
|
||||
}
|
||||
if message.inlines != nil {
|
||||
for _, inline := range message.inlines {
|
||||
payload.AddFile("inline", inline)
|
||||
}
|
||||
}
|
||||
|
||||
r := simplehttp.NewHTTPRequest(generateApiUrl(m, message.specific.endpoint()))
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
var response sendMessageResponse
|
||||
err = postResponseFromJSON(r, payload, &response)
|
||||
if err == nil {
|
||||
mes = response.Message
|
||||
id = response.Id
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (pm *plainMessage) addValues(p *simplehttp.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)
|
||||
}
|
||||
for _, bcc := range pm.bcc {
|
||||
p.AddValue("bcc", bcc)
|
||||
}
|
||||
if pm.html != "" {
|
||||
p.AddValue("html", pm.html)
|
||||
}
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) addValues(p *simplehttp.FormDataPayload) {
|
||||
p.AddReadCloser("message", "message.mime", mm.body)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) endpoint() string {
|
||||
return messagesEndpoint
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) endpoint() string {
|
||||
return mimeMessagesEndpoint
|
||||
}
|
||||
|
||||
// yesNo translates a true/false boolean value into a yes/no setting suitable for the Mailgun API.
|
||||
func yesNo(b bool) string {
|
||||
if b {
|
||||
return "yes"
|
||||
} else {
|
||||
return "no"
|
||||
}
|
||||
}
|
||||
|
||||
// isValid returns true if, and only if,
|
||||
// a Message instance is sufficiently initialized to send via the Mailgun interface.
|
||||
func isValid(m *Message) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !m.specific.isValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
if !validateStringList(m.to, true) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !validateStringList(m.tags, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !validateStringList(m.campaigns, false) || len(m.campaigns) > 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (pm *plainMessage) isValid() bool {
|
||||
if pm.from == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !validateStringList(pm.cc, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !validateStringList(pm.bcc, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if pm.text == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) isValid() bool {
|
||||
return mm.body != nil
|
||||
}
|
||||
|
||||
// validateStringList returns true if, and only if,
|
||||
// a slice of strings exists AND all of its elements exist,
|
||||
// OR if the slice doesn't exist AND it's not required to exist.
|
||||
// The requireOne parameter indicates whether the list is required to exist.
|
||||
func validateStringList(list []string, requireOne bool) bool {
|
||||
hasOne := false
|
||||
|
||||
if list == nil {
|
||||
return !requireOne
|
||||
} else {
|
||||
for _, a := range list {
|
||||
if a == "" {
|
||||
return false
|
||||
} else {
|
||||
hasOne = hasOne || true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasOne
|
||||
}
|
||||
|
||||
// GetStoredMessage retrieves information about a received e-mail message.
|
||||
// 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())
|
||||
|
||||
var response StoredMessage
|
||||
err := getResponseFromJSON(r, &response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// GetStoredMessageRaw retrieves the raw MIME body of a received e-mail message.
|
||||
// Compared to GetStoredMessage, it gives access to the unparsed MIME body, and
|
||||
// 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")
|
||||
|
||||
var response StoredMessageRaw
|
||||
err := getResponseFromJSON(r, &response)
|
||||
return response, err
|
||||
|
||||
}
|
||||
|
||||
// DeleteStoredMessage removes a previously stored message.
|
||||
// Note that Mailgun institutes a policy of automatically deleting messages after a set time.
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
152
Godeps/_workspace/src/github.com/mailgun/mailgun-go/rest_shim.go
generated
vendored
Normal file
152
Godeps/_workspace/src/github.com/mailgun/mailgun-go/rest_shim.go
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mbanzon/simplehttp"
|
||||
)
|
||||
|
||||
// The MailgunGoUserAgent identifies the client to the server, for logging purposes.
|
||||
// In the event of problems requiring a human administrator's assistance,
|
||||
// this user agent allows them to identify the client from human-generated activity.
|
||||
const MailgunGoUserAgent = "mailgun-go/1.0.0"
|
||||
|
||||
// This error will be returned whenever a Mailgun API returns an error response.
|
||||
// Your application can check the Actual field to see the actual HTTP response code returned.
|
||||
// URL contains the base URL accessed, sans any query parameters.
|
||||
type UnexpectedResponseError struct {
|
||||
Expected []int
|
||||
Actual int
|
||||
URL 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.
|
||||
func (e *UnexpectedResponseError) String() string {
|
||||
return fmt.Sprintf("UnexpectedResponseError URL=%s ExpectedOneOf=%#v Got=%d", e.URL, e.Expected, e.Actual)
|
||||
}
|
||||
|
||||
// Error() performs as String().
|
||||
func (e *UnexpectedResponseError) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// newError creates a new error condition to be returned.
|
||||
func newError(url string, expected []int, got int) error {
|
||||
return &UnexpectedResponseError{
|
||||
URL: url,
|
||||
Expected: expected,
|
||||
Actual: got,
|
||||
}
|
||||
}
|
||||
|
||||
// notGood searches a list of response codes (the haystack) for a matching entry (the needle).
|
||||
// If found, the response code is considered good, and thus false is returned.
|
||||
// Otherwise true is returned.
|
||||
func notGood(needle int, haystack []int) bool {
|
||||
for _, i := range haystack {
|
||||
if needle == i {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// expected denotes the expected list of known-good HTTP response codes possible from the Mailgun API.
|
||||
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)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp.Code)
|
||||
}
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response.Code)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response.Code)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response.Code)
|
||||
}
|
||||
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()
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp.Code)
|
||||
}
|
||||
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)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp.Code)
|
||||
}
|
||||
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)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp.Code)
|
||||
}
|
||||
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()
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp.Code)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
127
Godeps/_workspace/src/github.com/mailgun/mailgun-go/routes.go
generated
vendored
Normal file
127
Godeps/_workspace/src/github.com/mailgun/mailgun-go/routes.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Route structure contains information on a configured or to-be-configured route.
|
||||
// The Priority field indicates how soon the route works relative to other configured routes.
|
||||
// Routes of equal priority are consulted in chronological order.
|
||||
// The Description field provides a human-readable description for the route.
|
||||
// Mailgun ignores this field except to provide the description when viewing the Mailgun web control panel.
|
||||
// The Expression field lets you specify a pattern to match incoming messages against.
|
||||
// The Actions field contains strings specifying what to do
|
||||
// with any message which matches the provided expression.
|
||||
// The CreatedAt field provides a time-stamp for when the route came into existence.
|
||||
// Finally, the ID field provides a unique identifier for this route.
|
||||
//
|
||||
// When creating a new route, the SDK only uses a subset of the fields of this structure.
|
||||
// In particular, CreatedAt and ID are meaningless in this context, and will be ignored.
|
||||
// Only Priority, Description, Expression, and Actions need be provided.
|
||||
type Route struct {
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Expression string `json:"expression,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// GetRoutes returns the complete set of routes configured for your domain.
|
||||
// You use routes to configure how to handle returned messages, or
|
||||
// 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))
|
||||
if limit != DefaultLimit {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
|
||||
var envelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Route `json:"items"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
return envelope.TotalCount, envelope.Items, nil
|
||||
}
|
||||
|
||||
// CreateRoute installs a new route for your domain.
|
||||
// The route structure you provide serves as a template, and
|
||||
// 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)
|
||||
for _, action := range prototype.Actions {
|
||||
p.AddValue("action", action)
|
||||
}
|
||||
var envelope struct {
|
||||
Message string `json:"message"`
|
||||
*Route `json:"route"`
|
||||
}
|
||||
err := postResponseFromJSON(r, p, &envelope)
|
||||
return *envelope.Route, err
|
||||
}
|
||||
|
||||
// DeleteRoute removes the specified route from your domain's configuration.
|
||||
// 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())
|
||||
_, 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())
|
||||
var envelope struct {
|
||||
Message string `json:"message"`
|
||||
*Route `json:"route"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
return *envelope.Route, err
|
||||
}
|
||||
|
||||
// UpdateRoute provides an "in-place" update of the specified route.
|
||||
// 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()
|
||||
if route.Priority != 0 {
|
||||
p.AddValue("priority", strconv.Itoa(route.Priority))
|
||||
}
|
||||
if route.Description != "" {
|
||||
p.AddValue("description", route.Description)
|
||||
}
|
||||
if route.Expression != "" {
|
||||
p.AddValue("expression", route.Expression)
|
||||
}
|
||||
if route.Actions != nil {
|
||||
for _, action := range route.Actions {
|
||||
p.AddValue("action", action)
|
||||
}
|
||||
}
|
||||
// For some reason, this API function just returns a bare Route on success.
|
||||
// Unsure why this is the case; it seems like it ought to be a bug.
|
||||
var envelope Route
|
||||
err := putResponseFromJSON(r, p, &envelope)
|
||||
return envelope, err
|
||||
}
|
78
Godeps/_workspace/src/github.com/mailgun/mailgun-go/spam_complaints.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/mailgun/mailgun-go/spam_complaints.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
complaintsEndpoint = "complaints"
|
||||
)
|
||||
|
||||
// Complaint structures track how many times one of your emails have been marked as spam.
|
||||
// CreatedAt indicates when the first report arrives from a given recipient, identified by Address.
|
||||
// Count provides a running counter of how many times
|
||||
// the recipient thought your messages were not solicited.
|
||||
type Complaint struct {
|
||||
Count int `json:"count"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type complaintsEnvelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Complaint `json:"items"`
|
||||
}
|
||||
|
||||
// GetComplaints returns a set of spam complaints registered against your domain.
|
||||
// Recipients of your messages can click on a link which sends feedback to Mailgun
|
||||
// indicating that the message they received is, to them, spam.
|
||||
func (m *MailgunImpl) GetComplaints(limit, skip int) (int, []Complaint, error) {
|
||||
r := simplehttp.NewHTTPRequest(generateApiUrl(m, complaintsEndpoint))
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
if limit != -1 {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != -1 {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
|
||||
var envelope complaintsEnvelope
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
return envelope.TotalCount, envelope.Items, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
var c Complaint
|
||||
err := getResponseFromJSON(r, &c)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
_, err := makePostRequest(r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
59
Godeps/_workspace/src/github.com/mailgun/mailgun-go/stats.go
generated
vendored
Normal file
59
Godeps/_workspace/src/github.com/mailgun/mailgun-go/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Stat struct {
|
||||
Event string `json:"event"`
|
||||
TotalCount int `json:"total_count"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Id string `json:"id"`
|
||||
Tags map[string]int `json:"tags"`
|
||||
}
|
||||
|
||||
type statsEnvelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Stat `json:"items"`
|
||||
}
|
||||
|
||||
// GetStats returns a basic set of statistics for different events.
|
||||
// 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))
|
||||
|
||||
if limit != -1 {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != -1 {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
|
||||
if startDate != nil {
|
||||
r.AddParameter("start-date", startDate.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
for _, e := range event {
|
||||
r.AddParameter("event", e)
|
||||
}
|
||||
r.SetBasicAuth(basicAuthUser, m.ApiKey())
|
||||
|
||||
var res statsEnvelope
|
||||
err := getResponseFromJSON(r, &res)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
} else {
|
||||
return res.TotalCount, res.Items, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
66
Godeps/_workspace/src/github.com/mailgun/mailgun-go/unsubscribes.go
generated
vendored
Normal file
66
Godeps/_workspace/src/github.com/mailgun/mailgun-go/unsubscribes.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
package mailgun
|
||||
|
||||
import (
|
||||
"github.com/mbanzon/simplehttp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Unsubscription struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
Tag string `json:"tag"`
|
||||
ID string `json:"id"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// 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))
|
||||
if limit != DefaultLimit {
|
||||
r.AddParameter("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if skip != DefaultSkip {
|
||||
r.AddParameter("skip", strconv.Itoa(skip))
|
||||
}
|
||||
r.SetBasicAuth(basicAuthUser, mg.ApiKey())
|
||||
var envelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Unsubscription `json:"items"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
return envelope.TotalCount, envelope.Items, err
|
||||
}
|
||||
|
||||
// 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())
|
||||
var envelope struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Unsubscription `json:"items"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
return envelope.TotalCount, envelope.Items, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
_, err := makePostRequest(r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveUnsubscribe removes the e-mail address given from the domain's unsubscription table.
|
||||
// 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())
|
||||
_, err := makeDeleteRequest(r)
|
||||
return err
|
||||
}
|
68
Godeps/_workspace/src/github.com/mailgun/mailgun-go/webhooks.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/mailgun/mailgun-go/webhooks.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
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())
|
||||
var envelope struct {
|
||||
Webhooks map[string]interface{} `json:"webhooks"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
hooks := make(map[string]string, 0)
|
||||
if err != nil {
|
||||
return hooks, err
|
||||
}
|
||||
for k, v := range envelope.Webhooks {
|
||||
object := v.(map[string]interface{})
|
||||
url := object["url"]
|
||||
hooks[k] = url.(string)
|
||||
}
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
_, 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())
|
||||
_, 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())
|
||||
var envelope struct {
|
||||
Webhook struct {
|
||||
Url *string `json:"url"`
|
||||
} `json:"webhook"`
|
||||
}
|
||||
err := getResponseFromJSON(r, &envelope)
|
||||
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)
|
||||
_, err := makePutRequest(r, p)
|
||||
return err
|
||||
}
|
1
Godeps/_workspace/src/github.com/mbanzon/simplehttp/.gitignore
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/mbanzon/simplehttp/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
desktop.ini
|
11
Godeps/_workspace/src/github.com/mbanzon/simplehttp/.travis.yml
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/mbanzon/simplehttp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
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
|
27
Godeps/_workspace/src/github.com/mbanzon/simplehttp/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/mbanzon/simplehttp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
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.
|
15
Godeps/_workspace/src/github.com/mbanzon/simplehttp/README.md
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/mbanzon/simplehttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
simplehttp
|
||||
==========
|
||||
|
||||
[](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.
|
17
Godeps/_workspace/src/github.com/mbanzon/simplehttp/helpers.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/mbanzon/simplehttp/helpers.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
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)
|
||||
}
|
133
Godeps/_workspace/src/github.com/mbanzon/simplehttp/helpers_test.go
generated
vendored
Normal file
133
Godeps/_workspace/src/github.com/mbanzon/simplehttp/helpers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
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()
|
||||
}
|
||||
}
|
31
Godeps/_workspace/src/github.com/mbanzon/simplehttp/json_utils.go
generated
vendored
Normal file
31
Godeps/_workspace/src/github.com/mbanzon/simplehttp/json_utils.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
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
|
||||
}
|
15
Godeps/_workspace/src/github.com/mbanzon/simplehttp/parsing.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/mbanzon/simplehttp/parsing.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
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)
|
||||
}
|
38
Godeps/_workspace/src/github.com/mbanzon/simplehttp/payload_test.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/mbanzon/simplehttp/payload_test.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
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)
|
||||
}
|
141
Godeps/_workspace/src/github.com/mbanzon/simplehttp/payloads.go
generated
vendored
Normal file
141
Godeps/_workspace/src/github.com/mbanzon/simplehttp/payloads.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
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"
|
||||
}
|
74
Godeps/_workspace/src/github.com/mbanzon/simplehttp/shorthand.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/mbanzon/simplehttp/shorthand.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
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
|
||||
}
|
||||
}
|
104
Godeps/_workspace/src/github.com/mbanzon/simplehttp/shorthand_test.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/mbanzon/simplehttp/shorthand_test.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
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()
|
||||
}
|
||||
}
|
147
Godeps/_workspace/src/github.com/mbanzon/simplehttp/simplehttp.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/mbanzon/simplehttp/simplehttp.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
// 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
|
||||
}
|
37
Godeps/_workspace/src/github.com/mbanzon/simplehttp/simplehttp_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/mbanzon/simplehttp/simplehttp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
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()
|
||||
}
|
|
@ -108,6 +108,9 @@ func getClaims(r *http.Request) Claims {
|
|||
if con != nil {
|
||||
claims = con.(Claims)
|
||||
}
|
||||
claims.Ref = r.Header.Get("Origin")
|
||||
origin := r.Header.Get("Origin")
|
||||
if origin != "" {
|
||||
claims.Ref = origin
|
||||
}
|
||||
return claims
|
||||
}
|
||||
|
|
19
main.go
19
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
@ -13,12 +14,14 @@ import (
|
|||
"github.com/jmoiron/modl"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mailgun/mailgun-go"
|
||||
)
|
||||
|
||||
var (
|
||||
DB = &modl.DbMap{Dialect: modl.PostgresDialect{}}
|
||||
DBH modl.SqlExecutor = DB
|
||||
schemaDecoder = schema.NewDecoder()
|
||||
mgAccts = make(map[string]mailgun.Mailgun)
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -84,6 +87,22 @@ func main() {
|
|||
func cmdServe(c *cli.Context) {
|
||||
var err error
|
||||
|
||||
// Set up Mailgun handlers:
|
||||
// [{"ref":"hymenobacter","domain":"hymenobacter.info","public":"abc","private":"123"}]
|
||||
type account struct {
|
||||
Ref string
|
||||
Domain string
|
||||
Public string
|
||||
Private string
|
||||
}
|
||||
var accounts []account
|
||||
json.Unmarshal([]byte(os.Getenv("ACCOUNT_KEYS")), &accounts)
|
||||
log.Printf("Mailgun: %+v", accounts)
|
||||
|
||||
for _, a := range accounts {
|
||||
mgAccts[a.Ref] = mailgun.NewMailgun(a.Domain, a.Private, a.Public)
|
||||
}
|
||||
|
||||
addr := os.Getenv("PORT")
|
||||
if addr == "" {
|
||||
addr = "8901"
|
||||
|
|
36
users.go
36
users.go
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mailgun/mailgun-go"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
@ -136,7 +137,7 @@ func (u UserService) list(val *url.Values) (entity, *appError) {
|
|||
sql := `SELECT id, email, 'password' AS password, name, role,
|
||||
created_at, updated_at, deleted_at
|
||||
FROM users
|
||||
WHERE verified IS NOT FALSE
|
||||
WHERE verified IS TRUE
|
||||
AND deleted_at IS NULL;`
|
||||
if err := DBH.Select(&users, sql); err != nil {
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
|
@ -150,7 +151,7 @@ func (u UserService) get(id int64, genus string) (entity, *appError) {
|
|||
created_at, updated_at, deleted_at
|
||||
FROM users
|
||||
WHERE id=$1
|
||||
AND verified IS NOT FALSE
|
||||
AND verified IS TRUE
|
||||
AND deleted_at IS NULL;`
|
||||
if err := DBH.SelectOne(&user, q, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
@ -213,13 +214,36 @@ func (u UserService) create(e *entity, claims Claims) *appError {
|
|||
return newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Send out confirmation email
|
||||
mg, ok := mgAccts[claims.Ref]
|
||||
if ok {
|
||||
sender := fmt.Sprintf("%s Admin <admin@%s>", mg.Domain(), mg.Domain())
|
||||
recipient := fmt.Sprintf("%s <%s>", user.Name, user.Email)
|
||||
subject := fmt.Sprintf("New Account Confirmation - %s", mg.Domain())
|
||||
message := fmt.Sprintf("You are receiving this message because this email "+
|
||||
"address was used to sign up for an account at %s. Please visit this "+
|
||||
"URL to complete the sign up process: %s/users/new/verify/%s. If you "+
|
||||
"did not request an account, please disregard this message.",
|
||||
mg.Domain(), claims.Ref, nonce)
|
||||
m := mailgun.NewMessage(sender, subject, message, recipient)
|
||||
_, _, err := mg.Send(m)
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err)
|
||||
return newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// for thermokarst/jwt: authentication callback
|
||||
func dbAuthenticate(email string, password string) error {
|
||||
var user User
|
||||
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
|
||||
q := `SELECT *
|
||||
FROM users
|
||||
WHERE lower(email)=lower($1)
|
||||
AND verified IS TRUE
|
||||
AND deleted_at IS NULL;`
|
||||
if err := DBH.SelectOne(&user, q, email); err != nil {
|
||||
return ErrInvalidEmailOrPassword
|
||||
}
|
||||
|
@ -232,7 +256,11 @@ func dbAuthenticate(email string, password string) error {
|
|||
// for thermokarst/jwt: setting user in claims bundle
|
||||
func dbGetUserByEmail(email string) (*User, error) {
|
||||
var user User
|
||||
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
|
||||
q := `SELECT *
|
||||
FROM users
|
||||
WHERE lower(email)=lower($1)
|
||||
AND verified IS TRUE
|
||||
AND deleted_at IS NULL;`
|
||||
if err := DBH.SelectOne(&user, q, email); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrUserNotFound
|
||||
|
|
Reference in a new issue