From 965132988e3274c0a369526e776441be63a5cec5 Mon Sep 17 00:00:00 2001 From: "Derrick J. Wippler" Date: Mon, 14 Jan 2019 14:04:59 -0600 Subject: [PATCH] DomainList() now returns an iterator * Updated godoc documentation * Removed more deprecated types * Renamed ApiBase to APIBase * Removed gobuffalo/envy dependency * Updated copyright to 2019 --- Makefile | 10 ++ README.md | 3 +- bounces.go | 13 +- cmd/mailgun/main.go | 2 +- credentials.go | 9 +- domains.go | 187 ++++++++++++++++++++----- domains_test.go | 26 ++-- email_validation.go | 43 +++--- events.go | 57 +++----- events_test.go | 2 +- go.mod | 10 +- go.sum | 316 +++--------------------------------------- mailgun.go | 95 +++++-------- mailing_lists.go | 28 ++-- mailing_lists_test.go | 4 +- members.go | 2 +- messages.go | 1 + mock.go | 4 + mock_domains.go | 34 ++++- mock_mailing_list.go | 4 +- parse.go | 7 +- routes.go | 7 +- routes_test.go | 4 +- spam_complaints.go | 2 - stats.go | 14 +- template.go | 15 +- template_test.go | 1 - template_versions.go | 5 + version.go | 1 + 29 files changed, 379 insertions(+), 527 deletions(-) diff --git a/Makefile b/Makefile index 83968a9e..40ae78f8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ .PHONY: all .DEFAULT_GOAL := all +PACKAGE := github.com/mailgun/mailgun-go + gen: rm events/events_easyjson.go easyjson --all events/events.go @@ -9,3 +11,11 @@ gen: all: export GO111MODULE=on; go test . -v + +godoc: + mkdir -p /tmp/tmpgoroot/doc + -rm -rf /tmp/tmpgopath/src/${PACKAGE} + mkdir -p /tmp/tmpgopath/src/${PACKAGE} + tar -c --exclude='.git' --exclude='tmp' . | tar -x -C /tmp/tmpgopath/src/${PACKAGE} + echo -e "open http://localhost:6060/pkg/${PACKAGE}\n" + GOROOT=/tmp/tmpgoroot/ GOPATH=/tmp/tmpgopath/ godoc -http=localhost:6060 diff --git a/README.md b/README.md index 9545add2..216431ac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Mailgun with Go -[![GoDoc](https://godoc.org/gopkg.in/mailgun/mailgun-go.v1?status.svg)](https://godoc.org/gopkg.in/mailgun/mailgun-go.v1) +[![GoDoc](https://godoc.org/github.com/mailgun/mailgun-go?status.svg)](https://godoc.org/github.com/mailgun/mailgun-go) +[![Build Status](https://img.shields.io/travis/mailgun/mailgun-go/master.svg)](https://travis-ci.org/mailgun/mailgun-go) Go library for interacting with the [Mailgun](https://mailgun.com/) [API](https://documentation.mailgun.com/api_reference.html). diff --git a/bounces.go b/bounces.go index 70f1cda1..3ac0db1b 100644 --- a/bounces.go +++ b/bounces.go @@ -7,14 +7,15 @@ import ( // 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 { + // The time at which Mailgun detected the bounce. CreatedAt RFC2822Time `json:"created_at"` - Code string `json:"code"` - Address string `json:"address"` - Error string `json:"error"` + // Code provides the SMTP error code that caused the bounce + Code string `json:"code"` + // Address the bounce is for + Address string `json:"address"` + // human readable reason why + Error string `json:"error"` } type Paging struct { diff --git a/cmd/mailgun/main.go b/cmd/mailgun/main.go index 8f7325b5..f1bf1bee 100644 --- a/cmd/mailgun/main.go +++ b/cmd/mailgun/main.go @@ -26,7 +26,7 @@ func main() { parser := args.NewParser(args.EnvPrefix("MG_"), args.Desc(desc, args.IsFormated)) parser.AddOption("--verbose").Alias("-v").IsTrue().Help("be verbose") - parser.AddOption("--url").Env("URL").Default(mailgun.ApiBase).Help("url to the mailgun api") + parser.AddOption("--url").Env("URL").Default(mailgun.APIBase).Help("url to the mailgun api") parser.AddOption("--api-key").Env("API_KEY").Help("mailgun api key") parser.AddOption("--public-api-key").Env("PUBLIC_API_KEY").Help("mailgun public api key") parser.AddOption("--domain").Env("DOMAIN").Help("mailgun domain name") diff --git a/credentials.go b/credentials.go index c5b61418..6b67b2be 100644 --- a/credentials.go +++ b/credentials.go @@ -14,11 +14,12 @@ type Credential struct { } type credentialsListResponse struct { + // is -1 if Next() or First() have not been called TotalCount int `json:"total_count"` Items []Credential `json:"items"` } -// ErrEmptyParam results occur when a required parameter is missing. +// Returned when a required parameter is missing. var ErrEmptyParam = fmt.Errorf("empty or illegal parameter") // ListCredentials returns the (possibly zero-length) list of credentials associated with your domain. @@ -50,12 +51,6 @@ func (ri *CredentialsIterator) Err() error { return ri.err } -// Return the total number of items as returned by the mailgun API -// Returns -1 if Next() or First() have not been called -func (ri *CredentialsIterator) Count() int { - return ri.TotalCount -} - // Returns the current offset of the iterator func (ri *CredentialsIterator) Offset() int { return ri.offset diff --git a/domains.go b/domains.go index af686e4a..62647abe 100644 --- a/domains.go +++ b/domains.go @@ -6,20 +6,14 @@ import ( "strings" ) -// DefaultLimit and DefaultSkip instruct the SDK to rely on Mailgun's reasonable defaults for Paging settings. +// Use these to specify a spam action when creating a new domain. 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 ( - SpamActionTag = SpamAction("tag") + // Tag the received message with headers providing a measure of its spamness. + SpamActionTag = SpamAction("tag") + // Prevents Mailgun from taking any action on what it perceives to be spam. SpamActionDisabled = SpamAction("disabled") - SpamActionDelete = SpamAction("delete") + // instructs Mailgun to just block or delete the message all-together. + SpamActionDelete = SpamAction("delete") ) type SpamAction string @@ -31,9 +25,8 @@ type Domain struct { Name string `json:"name"` SMTPPassword string `json:"smtp_password"` Wildcard bool `json:"wildcard"` - // The SpamAction field must be one of Tag, Disabled, or Delete. - SpamAction string `json:"spam_action"` - State string `json:"state"` + SpamAction SpamAction `json:"spam_action"` + State string `json:"state"` } // DNSRecord structures describe intended records to properly configure your domain for use with Mailgun. @@ -56,22 +49,26 @@ type domainConnectionResponse struct { Connection DomainConnection `json:"connection"` } -type domainListResponse struct { +type domainsListResponse struct { + // is -1 if Next() or First() have not been called TotalCount int `json:"total_count"` Items []Domain `json:"items"` } +// Specify the domain connection options type DomainConnection struct { RequireTLS bool `json:"require_tls"` SkipVerification bool `json:"skip_verification"` } +// Specify the domain tracking options type DomainTracking struct { Click TrackingStatus `json:"click"` Open TrackingStatus `json:"open"` Unsubscribe TrackingStatus `json:"unsubscribe"` } +// The tracking status of a domain type TrackingStatus struct { Active bool `json:"active"` HTMLFooter string `json:"html_footer"` @@ -83,30 +80,151 @@ type domainTrackingResponse struct { } // ListDomains 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 (mg *MailgunImpl) ListDomains(ctx context.Context, opts *ListOptions) (int, []Domain, error) { - r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint)) - r.setClient(mg.Client()) - r.setBasicAuth(basicAuthUser, mg.APIKey()) +func (mg *MailgunImpl) ListDomains(ctx context.Context, opts *ListOptions) *DomainsIterator { + var limit int + if opts != nil { + limit = opts.Limit + } + return &DomainsIterator{ + mg: mg, + url: generatePublicApiUrl(mg, domainsEndpoint), + domainsListResponse: domainsListResponse{TotalCount: -1}, + limit: limit, + } +} + +type DomainsIterator struct { + domainsListResponse + + limit int + mg Mailgun + offset int + url string + err error +} + +// If an error occurred during iteration `Err()` will return non nil +func (ri *DomainsIterator) Err() error { + return ri.err +} + +// Returns the current offset of the iterator +func (ri *DomainsIterator) Offset() int { + return ri.offset +} + +// Retrieves the next page of items from the api. Returns false when there +// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve +// the error +func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool { + if ri.err != nil { + return false + } + + ri.err = ri.fetch(ctx, ri.offset, ri.limit) + if ri.err != nil { + return false + } + + cpy := make([]Domain, len(ri.Items)) + copy(cpy, ri.Items) + *items = cpy + if len(ri.Items) == 0 { + return false + } + ri.offset = len(ri.Items) + return true +} + +// Retrieves the first page of items from the api. Returns false if there +// was an error. It also sets the iterator object to the first page. +// Use `.Err()` to retrieve the error. +func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool { + if ri.err != nil { + return false + } + ri.err = ri.fetch(ctx, 0, ri.limit) + if ri.err != nil { + return false + } + cpy := make([]Domain, len(ri.Items)) + copy(cpy, ri.Items) + *items = cpy + ri.offset = len(ri.Items) + return true +} + +// Retrieves the last page of items from the api. +// Calling Last() is invalid unless you first call First() or Next() +// Returns false if there was an error. It also sets the iterator object +// to the last page. Use `.Err()` to retrieve the error. +func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool { + if ri.err != nil { + return false + } + + if ri.TotalCount == -1 { + return false + } - if opts != nil && opts.Limit != 0 { - r.addParameter("limit", strconv.Itoa(opts.Limit)) + ri.offset = ri.TotalCount - ri.limit + if ri.offset < 0 { + ri.offset = 0 } - if opts != nil && opts.Skip != 0 { - r.addParameter("skip", strconv.Itoa(opts.Skip)) + ri.err = ri.fetch(ctx, ri.offset, ri.limit) + if ri.err != nil { + return false + } + cpy := make([]Domain, len(ri.Items)) + copy(cpy, ri.Items) + *items = cpy + return true +} + +// Retrieves the previous page of items from the api. Returns false when there +// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve +// the error if any +func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool { + if ri.err != nil { + return false } - var list domainListResponse - err := getResponseFromJSON(ctx, r, &list) - if err != nil { - return -1, nil, err + if ri.TotalCount == -1 { + return false } - return list.TotalCount, list.Items, nil + + ri.offset = ri.offset - ri.limit + if ri.offset < 0 { + ri.offset = 0 + } + + ri.err = ri.fetch(ctx, ri.offset, ri.limit) + if ri.err != nil { + return false + } + cpy := make([]Domain, len(ri.Items)) + copy(cpy, ri.Items) + *items = cpy + if len(ri.Items) == 0 { + return false + } + return true +} + +func (ri *DomainsIterator) fetch(ctx context.Context, skip, limit int) error { + r := newHTTPRequest(ri.url) + r.setBasicAuth(basicAuthUser, ri.mg.APIKey()) + r.setClient(ri.mg.Client()) + + if skip != 0 { + r.addParameter("skip", strconv.Itoa(skip)) + } + if limit != 0 { + r.addParameter("limit", strconv.Itoa(limit)) + } + + return getResponseFromJSON(ctx, r, &ri.domainsListResponse) } // Retrieve detailed information about the named domain. @@ -119,6 +237,7 @@ func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string) (Domain, [] return resp.Domain, resp.ReceivingDNSRecords, resp.SendingDNSRecords, err } +// Optional parameters when creating a domain type CreateDomainOptions struct { SpamAction SpamAction Wildcard bool diff --git a/domains_test.go b/domains_test.go index 9ab836e1..9e21c8c4 100644 --- a/domains_test.go +++ b/domains_test.go @@ -14,19 +14,21 @@ const ( testKey = "api-fake-key" ) -func TestGetDomains(t *testing.T) { +func TestListDomains(t *testing.T) { mg := mailgun.NewMailgun(testDomain, testKey) mg.SetAPIBase(server.URL()) ctx := context.Background() - n, domains, err := mg.ListDomains(ctx, nil) - ensure.Nil(t, err) - ensure.DeepEqual(t, len(domains) != 0, true) - - t.Logf("TestGetDomains: %d domains retrieved\n", n) - for _, d := range domains { - t.Logf("TestGetDomains: %#v\n", d) + it := mg.ListDomains(ctx, nil) + var page []mailgun.Domain + for it.Next(ctx, &page) { + for _, d := range page { + t.Logf("TestListDomains: %#v\n", d) + } } + t.Logf("TestListDomains: %d domains retrieved\n", it.TotalCount) + ensure.Nil(t, it.Err()) + ensure.True(t, it.TotalCount != 0) } func TestGetSingleDomain(t *testing.T) { @@ -34,10 +36,12 @@ func TestGetSingleDomain(t *testing.T) { mg.SetAPIBase(server.URL()) ctx := context.Background() - _, domains, err := mg.ListDomains(ctx, nil) - ensure.Nil(t, err) + it := mg.ListDomains(ctx, nil) + var page []mailgun.Domain + ensure.True(t, it.Next(ctx, &page)) + ensure.Nil(t, it.Err()) - dr, rxDnsRecords, txDnsRecords, err := mg.GetDomain(ctx, domains[0].Name) + dr, rxDnsRecords, txDnsRecords, err := mg.GetDomain(ctx, page[0].Name) ensure.Nil(t, err) ensure.DeepEqual(t, len(rxDnsRecords) != 0, true) ensure.DeepEqual(t, len(txDnsRecords) != 0, true) diff --git a/email_validation.go b/email_validation.go index 825112a3..5eff7c67 100644 --- a/email_validation.go +++ b/email_validation.go @@ -23,27 +23,26 @@ type EmailVerificationParts struct { // 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. -// MailboxVerification indicates whether an email address is deliverable. -// 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. -// IsDisposableAddress indicates whether Mailgun thinks the address is from a known -// disposable mailbox provider. -// IsRoleAddress indicates whether Mailgun thinks the address is an email distribution list. -// Reason contains error's description. type EmailVerification struct { - IsValid bool `json:"is_valid"` - MailboxVerification string `json:"mailbox_verification"` - Parts EmailVerificationParts `json:"parts"` - Address string `json:"address"` - DidYouMean string `json:"did_you_mean"` - IsDisposableAddress bool `json:"is_disposable_address"` - IsRoleAddress bool `json:"is_role_address"` - Reason string `json:"reason"` + // Indicates whether an email address conforms to IETF RFC standards. + IsValid bool `json:"is_valid"` + // Indicates whether an email address is deliverable. + MailboxVerification string `json:"mailbox_verification"` + // Parts records the different subfields of the parsed email address + Parts EmailVerificationParts `json:"parts"` + // Echoes the address provided. + Address string `json:"address"` + // Provides a simple recommendation in case the address is invalid or + // Mailgun thinks you might have a typo. May be empty, in which case + // Mailgun has no recommendation to give. + DidYouMean string `json:"did_you_mean"` + // Indicates whether Mailgun thinks the address is from a known + // disposable mailbox provider. + IsDisposableAddress bool `json:"is_disposable_address"` + // Indicates whether Mailgun thinks the address is an email distribution list. + IsRoleAddress bool `json:"is_role_address"` + // A human readable reason the address is reported as invalid + Reason string `json:"reason"` } type addressParseResult struct { @@ -77,7 +76,7 @@ func NewEmailValidator(apiKey string) *EmailValidatorImpl { return &EmailValidatorImpl{ client: http.DefaultClient, isPublicKey: isPublicKey, - apiBase: ApiBase, + apiBase: APIBase, apiKey: apiKey, } } @@ -102,7 +101,7 @@ func NewEmailValidatorFromEnv() (*EmailValidatorImpl, error) { return v, nil } -// ApiBase returns the API Base URL configured for this client. +// APIBase returns the API Base URL configured for this client. func (m *EmailValidatorImpl) APIBase() string { return m.apiBase } diff --git a/events.go b/events.go index bf0b10df..373f0954 100644 --- a/events.go +++ b/events.go @@ -28,14 +28,6 @@ type ListEventOptions struct { PollInterval time.Duration } -// Depreciated See `ListEvents()` -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.Response @@ -44,22 +36,6 @@ type EventIterator struct { } // Create an new iterator to fetch a page of events from the events api -// it := mg.ListEvents(&ListEventOptions{Limit: 100}) -// var events []Event -// for it.Next(&events) { -// for _, event := range events { -// // Do things with events -// switch event := e.(type) { -// case *events.Accepted: -// log.Printf("Accepted Event: %s - %v", event.Message.Headers.MessageID, event.GetTimestamp()) -// case *events.Delivered: -// log.Printf("Delivered Event: %s - %v", event.Message.Headers.MessageID, event.GetTimestamp()) -// } -// } -// } -// if it.Err() != nil { -// log.Fatal(it.Err()) -// } func (mg *MailgunImpl) ListEvents(opts *ListEventOptions) *EventIterator { req := newHTTPRequest(generateApiUrl(mg, eventsEndpoint)) if opts != nil { @@ -197,22 +173,25 @@ type EventPoller struct { } // Poll the events api and return new events as they occur -// it = mg.PollEvents(&ListEventOptions{ -// // Only events with a timestamp after this date/time will be returned -// Begin: time.Now().Add(time.Second * -3), -// // How often we poll the api for new events -// PollInterval: time.Second * 4}) -// var events []Event -// // Blocks until new events appear or context is cancelled +// it = mg.PollEvents(&ListEventOptions{ +// // Only events with a timestamp after this date/time will be returned +// Begin: time.Now().Add(time.Second * -3), +// // How often we poll the api for new events +// PollInterval: time.Second * 4 +// }) +// +// var events []Event // ctx, cancel := context.WithCancel(context.Background()) -// for it.Poll(ctx, &events) { -// for _, event := range(events) { -// fmt.Printf("Event %+v\n", event) -// } -// } -// if it.Err() != nil { -// log.Fatal(it.Err()) -// } +// +// // Blocks until new events appear or context is cancelled +// for it.Poll(ctx, &events) { +// for _, event := range(events) { +// fmt.Printf("Event %+v\n", event) +// } +// } +// if it.Err() != nil { +// log.Fatal(it.Err()) +// } func (mg *MailgunImpl) PollEvents(opts *ListEventOptions) *EventPoller { now := time.Now() // ForceAscending must be set diff --git a/events_test.go b/events_test.go index 9474273a..cfb0eae4 100644 --- a/events_test.go +++ b/events_test.go @@ -111,7 +111,7 @@ func TestEventPoller(t *testing.T) { ensure.DeepEqual(t, accepted.Recipient, "user@"+testDomain) } -func ExampleListEvents() { +func ExampleMailgunImpl_ListEvents() { mg := mailgun.NewMailgun("your-domain.com", "your-api-key") mg.SetAPIBase(server.URL()) diff --git a/go.mod b/go.mod index 47a81a05..f396a67c 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,23 @@ require ( github.com/fatih/structs v1.1.0 // indirect github.com/go-chi/chi v3.3.4+incompatible github.com/go-ini/ini v1.41.0 // indirect - github.com/gobuffalo/envy v1.6.11 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect + github.com/kr/pretty v0.1.0 // indirect github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 + github.com/onsi/ginkgo v1.7.0 // indirect + github.com/onsi/gomega v1.4.3 // indirect github.com/pkg/errors v0.8.1 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect + github.com/spf13/cast v1.3.0 // indirect github.com/thrawn01/args v0.3.0 + golang.org/x/net v0.0.0-20181207154023-610586996380 // indirect + golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect + golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/ini.v1 v1.41.0 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect ) replace github.com/mailgun/mailgun-go/v3/events => ./events diff --git a/go.sum b/go.sum index 64d250e7..8229b7cf 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,13 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/drhodes/golorem v0.0.0-20160418191928-ecccc744c2d9 h1:EQOZw/LCQ0SM4sNez3EhUf9gQalQrLrs4mPtmQa+d58= github.com/drhodes/golorem v0.0.0-20160418191928-ecccc744c2d9/go.mod h1:NsKVpF4h4j13Vm6Cx7Kf0V03aJKjfaStvm5rvK4+FyQ= -github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -28,326 +16,60 @@ github.com/go-chi/chi v3.3.4+incompatible h1:X+OApYAmoQS6jr1WoUgW+t5Ry5RYGXq2A// github.com/go-chi/chi v3.3.4+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-ini/ini v1.41.0 h1:526aoxDtxRHFQKMZfcX2OG9oOI8TJ5yPLM0Mkno/uTY= github.com/go-ini/ini v1.41.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/gobuffalo/buffalo v0.12.8-0.20181004233540-fac9bb505aa8/go.mod h1:sLyT7/dceRXJUxSsE813JTQtA3Eb1vjxWfo/N//vXIY= -github.com/gobuffalo/buffalo v0.13.0/go.mod h1:Mjn1Ba9wpIbpbrD+lIDMy99pQ0H0LiddMIIDGse7qT4= -github.com/gobuffalo/buffalo-plugins v1.0.2/go.mod h1:pOp/uF7X3IShFHyobahTkTLZaeUXwb0GrUTb9ngJWTs= -github.com/gobuffalo/buffalo-plugins v1.0.4/go.mod h1:pWS1vjtQ6uD17MVFWf7i3zfThrEKWlI5+PYLw/NaDB4= -github.com/gobuffalo/buffalo-plugins v1.4.3/go.mod h1:uCzTY0woez4nDMdQjkcOYKanngeUVRO2HZi7ezmAjWY= -github.com/gobuffalo/buffalo-plugins v1.5.1/go.mod h1:jbmwSZK5+PiAP9cC09VQOrGMZFCa/P0UMlIS3O12r5w= -github.com/gobuffalo/buffalo-plugins v1.6.4/go.mod h1:/+N1aophkA2jZ1ifB2O3Y9yGwu6gKOVMtUmJnbg+OZI= -github.com/gobuffalo/buffalo-plugins v1.6.5/go.mod h1:0HVkbgrVs/MnPZ/FOseDMVanCTm2RNcdM0PuXcL1NNI= -github.com/gobuffalo/buffalo-plugins v1.6.7/go.mod h1:ZGZRkzz2PiKWHs0z7QsPBOTo2EpcGRArMEym6ghKYgk= -github.com/gobuffalo/buffalo-plugins v1.6.9/go.mod h1:yYlYTrPdMCz+6/+UaXg5Jm4gN3xhsvsQ2ygVatZV5vw= -github.com/gobuffalo/buffalo-plugins v1.6.11/go.mod h1:eAA6xJIL8OuynJZ8amXjRmHND6YiusVAaJdHDN1Lu8Q= -github.com/gobuffalo/buffalo-plugins v1.8.2/go.mod h1:9te6/VjEQ7pKp7lXlDIMqzxgGpjlKoAcAANdCgoR960= -github.com/gobuffalo/buffalo-pop v1.0.5/go.mod h1:Fw/LfFDnSmB/vvQXPvcXEjzP98Tc+AudyNWUBWKCwQ8= -github.com/gobuffalo/envy v1.6.4/go.mod h1:Abh+Jfw475/NWtYMEt+hnJWRiC8INKWibIMyNt1w2Mc= -github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.6/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.7/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.8/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.9/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.10/go.mod h1:X0CFllQjTV5ogsnUrg+Oks2yTI+PU2dGYBJOEI2D1Uo= -github.com/gobuffalo/envy v1.6.11 h1:dCKSFypLRvqaaUtyzkfKKF2j35ce5agsqfyIrRmm02E= -github.com/gobuffalo/envy v1.6.11/go.mod h1:Fiq52W7nrHGDggFPhn2ZCcHw4u/rqXkqo+i7FB6EAcg= -github.com/gobuffalo/events v1.0.3/go.mod h1:Txo8WmqScapa7zimEQIwgiJBvMECMe9gJjsKNPN3uZw= -github.com/gobuffalo/events v1.0.7/go.mod h1:z8txf6H9jWhQ5Scr7YPLWg/cgXBRj8Q4uYI+rsVCCSQ= -github.com/gobuffalo/events v1.0.8/go.mod h1:A5KyqT1sA+3GJiBE4QKZibse9mtOcI9nw8gGrDdqYGs= -github.com/gobuffalo/events v1.1.3/go.mod h1:9yPGWYv11GENtzrIRApwQRMYSbUgCsZ1w6R503fCfrk= -github.com/gobuffalo/events v1.1.4/go.mod h1:09/YRRgZHEOts5Isov+g9X2xajxdvOAcUuAHIX/O//A= -github.com/gobuffalo/events v1.1.5/go.mod h1:3YUSzgHfYctSjEjLCWbkXP6djH2M+MLaVRzb4ymbAK0= -github.com/gobuffalo/events v1.1.7/go.mod h1:6fGqxH2ing5XMb3EYRq9LEkVlyPGs4oO/eLzh+S8CxY= -github.com/gobuffalo/events v1.1.8/go.mod h1:UFy+W6X6VbCWS8k2iT81HYX65dMtiuVycMy04cplt/8= -github.com/gobuffalo/fizz v1.0.12/go.mod h1:C0sltPxpYK8Ftvf64kbsQa2yiCZY4RZviurNxXdAKwc= -github.com/gobuffalo/flect v0.0.0-20180907193754-dc14d8acaf9f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181002182613-4571df4b1daf/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181007231023-ae7ed6bfe683/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181018182602-fd24a256709f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181019110701-3d6f0b585514/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181024204909-8f6be1a8c6c2/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181104133451-1f6e9779237a/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= -github.com/gobuffalo/flect v0.0.0-20181114183036-47375f6d8328/go.mod h1:0HvNbHdfh+WOvDSIASqJOSxTOWSxCCUF++k/Y53v9rI= -github.com/gobuffalo/genny v0.0.0-20180924032338-7af3a40f2252/go.mod h1:tUTQOogrr7tAQnhajMSH6rv1BVev34H2sa1xNHMy94g= -github.com/gobuffalo/genny v0.0.0-20181003150629-3786a0744c5d/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= -github.com/gobuffalo/genny v0.0.0-20181005145118-318a41a134cc/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= -github.com/gobuffalo/genny v0.0.0-20181007153042-b8de7d566757/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= -github.com/gobuffalo/genny v0.0.0-20181012161047-33e5f43d83a6/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= -github.com/gobuffalo/genny v0.0.0-20181017160347-90a774534246/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= -github.com/gobuffalo/genny v0.0.0-20181024195656-51392254bf53/go.mod h1:o9GEH5gn5sCKLVB5rHFC4tq40rQ3VRUzmx6WwmaqISE= -github.com/gobuffalo/genny v0.0.0-20181025145300-af3f81d526b8/go.mod h1:uZ1fFYvdcP8mu0B/Ynarf6dsGvp7QFIpk/QACUuFUVI= -github.com/gobuffalo/genny v0.0.0-20181027191429-94d6cfb5c7fc/go.mod h1:x7SkrQQBx204Y+O9EwRXeszLJDTaWN0GnEasxgLrQTA= -github.com/gobuffalo/genny v0.0.0-20181027195209-3887b7171c4f/go.mod h1:JbKx8HSWICu5zyqWOa0dVV1pbbXOHusrSzQUprW6g+w= -github.com/gobuffalo/genny v0.0.0-20181106193839-7dcb0924caf1/go.mod h1:x61yHxvbDCgQ/7cOAbJCacZQuHgB0KMSzoYcw5debjU= -github.com/gobuffalo/genny v0.0.0-20181107223128-f18346459dbe/go.mod h1:utQD3aKKEsdb03oR+Vi/6ztQb1j7pO10N3OBoowRcSU= -github.com/gobuffalo/genny v0.0.0-20181114215459-0a4decd77f5d/go.mod h1:kN2KZ8VgXF9VIIOj/GM0Eo7YK+un4Q3tTreKOf0q1ng= -github.com/gobuffalo/genny v0.0.0-20181119162812-e8ff4adce8bb/go.mod h1:BA9htSe4bZwBDJLe8CUkoqkypq3hn3+CkoHqVOW718E= -github.com/gobuffalo/genny v0.0.0-20181127225641-2d959acc795b/go.mod h1:l54xLXNkteX/PdZ+HlgPk1qtcrgeOr3XUBBPDbH+7CQ= -github.com/gobuffalo/genny v0.0.0-20181128191930-77e34f71ba2a/go.mod h1:FW/D9p7cEEOqxYA71/hnrkOWm62JZ5ZNxcNIVJEaWBU= -github.com/gobuffalo/genny v0.0.0-20181203165245-fda8bcce96b1/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= -github.com/gobuffalo/genny v0.0.0-20181203201232-849d2c9534ea/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= -github.com/gobuffalo/genny v0.0.0-20181206121324-d6fb8a0dbe36/go.mod h1:wpNSANu9UErftfiaAlz1pDZclrYzLtO5lALifODyjuM= -github.com/gobuffalo/genny v0.0.0-20181207164119-84844398a37d/go.mod h1:y0ysCHGGQf2T3vOhCrGHheYN54Y/REj0ayd0Suf4C/8= -github.com/gobuffalo/github_flavored_markdown v1.0.4/go.mod h1:uRowCdK+q8d/RF0Kt3/DSalaIXbb0De/dmTqMQdkQ4I= -github.com/gobuffalo/github_flavored_markdown v1.0.5/go.mod h1:U0643QShPF+OF2tJvYNiYDLDGDuQmJZXsf/bHOJPsMY= -github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6DktTPHkOtCbr+arAD5mkwMzXLI= -github.com/gobuffalo/httptest v1.0.2/go.mod h1:7T1IbSrg60ankme0aDLVnEY0h056g9M1/ZvpVThtB7E= -github.com/gobuffalo/licenser v0.0.0-20180924033006-eae28e638a42/go.mod h1:Ubo90Np8gpsSZqNScZZkVXXAo5DGhTb+WYFIjlnog8w= -github.com/gobuffalo/licenser v0.0.0-20181025145548-437d89de4f75/go.mod h1:x3lEpYxkRG/XtGCUNkio+6RZ/dlOvLzTI9M1auIwFcw= -github.com/gobuffalo/licenser v0.0.0-20181027200154-58051a75da95/go.mod h1:BzhaaxGd1tq1+OLKObzgdCV9kqVhbTulxOpYbvMQWS0= -github.com/gobuffalo/licenser v0.0.0-20181109171355-91a2a7aac9a7/go.mod h1:m+Ygox92pi9bdg+gVaycvqE8RVSjZp7mWw75+K5NPHk= -github.com/gobuffalo/licenser v0.0.0-20181128165715-cc7305f8abed/go.mod h1:oU9F9UCE+AzI/MueCKZamsezGOOHfSirltllOVeRTAE= -github.com/gobuffalo/licenser v0.0.0-20181203160806-fe900bbede07/go.mod h1:ph6VDNvOzt1CdfaWC+9XwcBnlSTBz2j49PBwum6RFaU= -github.com/gobuffalo/logger v0.0.0-20181022175615-46cfb361fc27/go.mod h1:8sQkgyhWipz1mIctHF4jTxmJh1Vxhp7mP8IqbljgJZo= -github.com/gobuffalo/logger v0.0.0-20181027144941-73d08d2bb969/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= -github.com/gobuffalo/logger v0.0.0-20181027193913-9cf4dd0efe46/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= -github.com/gobuffalo/logger v0.0.0-20181109185836-3feeab578c17/go.mod h1:oNErH0xLe+utO+OW8ptXMSA5DkiSEDW1u3zGIt8F9Ew= -github.com/gobuffalo/logger v0.0.0-20181117211126-8e9b89b7c264/go.mod h1:5etB91IE0uBlw9k756fVKZJdS+7M7ejVhmpXXiSFj0I= -github.com/gobuffalo/logger v0.0.0-20181127160119-5b956e21995c/go.mod h1:+HxKANrR9VGw9yN3aOAppJKvhO05ctDi63w4mDnKv2U= -github.com/gobuffalo/makr v1.1.5/go.mod h1:Y+o0btAH1kYAMDJW/TX3+oAXEu0bmSLLoC9mIFxtzOw= -github.com/gobuffalo/mapi v1.0.0/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/meta v0.0.0-20181018155829-df62557efcd3/go.mod h1:XTTOhwMNryif3x9LkTTBO/Llrveezd71u3quLd0u7CM= -github.com/gobuffalo/meta v0.0.0-20181018192820-8c6cef77dab3/go.mod h1:E94EPzx9NERGCY69UWlcj6Hipf2uK/vnfrF4QD0plVE= -github.com/gobuffalo/meta v0.0.0-20181025145500-3a985a084b0a/go.mod h1:YDAKBud2FP7NZdruCSlmTmDOZbVSa6bpK7LJ/A/nlKg= -github.com/gobuffalo/meta v0.0.0-20181114191255-b130ebedd2f7/go.mod h1:K6cRZ29ozr4Btvsqkjvg5nDFTLOgTqf03KA70Ks0ypE= -github.com/gobuffalo/meta v0.0.0-20181127070345-0d7e59dd540b/go.mod h1:RLO7tMvE0IAKAM8wny1aN12pvEKn7EtkBLkUZR00Qf8= -github.com/gobuffalo/mw-basicauth v1.0.3/go.mod h1:dg7+ilMZOKnQFHDefUzUHufNyTswVUviCBgF244C1+0= -github.com/gobuffalo/mw-contenttype v0.0.0-20180802152300-74f5a47f4d56/go.mod h1:7EvcmzBbeCvFtQm5GqF9ys6QnCxz2UM1x0moiWLq1No= -github.com/gobuffalo/mw-csrf v0.0.0-20180802151833-446ff26e108b/go.mod h1:sbGtb8DmDZuDUQoxjr8hG1ZbLtZboD9xsn6p77ppcHo= -github.com/gobuffalo/mw-forcessl v0.0.0-20180802152810-73921ae7a130/go.mod h1:JvNHRj7bYNAMUr/5XMkZaDcw3jZhUZpsmzhd//FFWmQ= -github.com/gobuffalo/mw-i18n v0.0.0-20180802152014-e3060b7e13d6/go.mod h1:91AQfukc52A6hdfIfkxzyr+kpVYDodgAeT5cjX1UIj4= -github.com/gobuffalo/mw-paramlogger v0.0.0-20181005191442-d6ee392ec72e/go.mod h1:6OJr6VwSzgJMqWMj7TYmRUqzNe2LXu/W1rRW4MAz/ME= -github.com/gobuffalo/mw-tokenauth v0.0.0-20181001105134-8545f626c189/go.mod h1:UqBF00IfKvd39ni5+yI5MLMjAf4gX7cDKN/26zDOD6c= -github.com/gobuffalo/packd v0.0.0-20181027182251-01ad393492c8/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= -github.com/gobuffalo/packd v0.0.0-20181027190505-aafc0d02c411/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= -github.com/gobuffalo/packd v0.0.0-20181027194105-7ae579e6d213/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= -github.com/gobuffalo/packd v0.0.0-20181031195726-c82734870264/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= -github.com/gobuffalo/packd v0.0.0-20181104210303-d376b15f8e96/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= -github.com/gobuffalo/packd v0.0.0-20181111195323-b2e760a5f0ff/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= -github.com/gobuffalo/packd v0.0.0-20181114190715-f25c5d2471d7/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= -github.com/gobuffalo/packd v0.0.0-20181124090624-311c6248e5fb/go.mod h1:Foenia9ZvITEvG05ab6XpiD5EfBHPL8A6hush8SJ0o8= -github.com/gobuffalo/packd v0.0.0-20181207120301-c49825f8f6f4/go.mod h1:LYc0TGKFBBFTRC9dg2pcRcMqGCTMD7T2BIMP7OBuQAA= -github.com/gobuffalo/packr v1.13.7/go.mod h1:KkinLIn/n6+3tVXMwg6KkNvWwVsrRAz4ph+jgpk3Z24= -github.com/gobuffalo/packr v1.15.0/go.mod h1:t5gXzEhIviQwVlNx/+3SfS07GS+cZ2hn76WLzPp6MGI= -github.com/gobuffalo/packr v1.15.1/go.mod h1:IeqicJ7jm8182yrVmNbM6PR4g79SjN9tZLH8KduZZwE= -github.com/gobuffalo/packr v1.19.0/go.mod h1:MstrNkfCQhd5o+Ct4IJ0skWlxN8emOq8DsoT1G98VIU= -github.com/gobuffalo/packr v1.20.0/go.mod h1:JDytk1t2gP+my1ig7iI4NcVaXr886+N0ecUga6884zw= -github.com/gobuffalo/packr v1.21.0/go.mod h1:H00jGfj1qFKxscFJSw8wcL4hpQtPe1PfU2wa6sg/SR0= -github.com/gobuffalo/packr/v2 v2.0.0-rc.8/go.mod h1:y60QCdzwuMwO2R49fdQhsjCPv7tLQFR0ayzxxla9zes= -github.com/gobuffalo/packr/v2 v2.0.0-rc.10/go.mod h1:4CWWn4I5T3v4c1OsJ55HbHlUEKNWMITG5iIkdr4Px4w= -github.com/gobuffalo/packr/v2 v2.0.0-rc.11/go.mod h1:JoieH/3h3U4UmatmV93QmqyPUdf4wVM9HELaHEu+3fk= -github.com/gobuffalo/plush v3.7.16+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.20+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.21+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.22+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.23+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.30+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plush v3.7.32+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= -github.com/gobuffalo/plushgen v0.0.0-20181128164830-d29dcb966cb2/go.mod h1:r9QwptTFnuvSaSRjpSp4S2/4e2D3tJhARYbvEBcKSb4= -github.com/gobuffalo/plushgen v0.0.0-20181203163832-9fc4964505c2/go.mod h1:opEdT33AA2HdrIwK1aibqnTJDVVKXC02Bar/GT1YRVs= -github.com/gobuffalo/plushgen v0.0.0-20181207152837-eedb135bd51b/go.mod h1:Lcw7HQbEVm09sAQrCLzIxuhFbB3nAgp4c55E+UlynR0= -github.com/gobuffalo/pop v4.8.2+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= -github.com/gobuffalo/pop v4.8.3+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= -github.com/gobuffalo/pop v4.8.4+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= -github.com/gobuffalo/release v1.0.35/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= -github.com/gobuffalo/release v1.0.38/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= -github.com/gobuffalo/release v1.0.42/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= -github.com/gobuffalo/release v1.0.52/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= -github.com/gobuffalo/release v1.0.53/go.mod h1:FdF257nd8rqhNaqtDWFGhxdJ/Ig4J7VcS3KL7n/a+aA= -github.com/gobuffalo/release v1.0.54/go.mod h1:Pe5/RxRa/BE8whDpGfRqSI7D1a0evGK1T4JDm339tJc= -github.com/gobuffalo/release v1.0.61/go.mod h1:mfIO38ujUNVDlBziIYqXquYfBF+8FDHUjKZgYC1Hj24= -github.com/gobuffalo/release v1.0.72/go.mod h1:NP5NXgg/IX3M5XmHmWR99D687/3Dt9qZtTK/Lbwc1hU= -github.com/gobuffalo/release v1.1.1/go.mod h1:Sluak1Xd6kcp6snkluR1jeXAogdJZpFFRzTYRs/2uwg= -github.com/gobuffalo/shoulders v1.0.1/go.mod h1:V33CcVmaQ4gRUmHKwq1fiTXuf8Gp/qjQBUL5tHPmvbA= -github.com/gobuffalo/syncx v0.0.0-20181120191700-98333ab04150/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobuffalo/syncx v0.0.0-20181120194010-558ac7de985f/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobuffalo/tags v2.0.11+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= -github.com/gobuffalo/tags v2.0.14+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= -github.com/gobuffalo/uuid v2.0.3+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= -github.com/gobuffalo/uuid v2.0.4+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= -github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= -github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= -github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOreDJznWevcn8NTmQEW5STSBgIkpkjzqXc= -github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= -github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.1.2/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= -github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= -github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= -github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= -github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= -github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c= -github.com/markbates/grift v1.0.4/go.mod h1:wbmtW74veyx+cgfwFhlnnMWqhoz55rnHR47oMXzsyVs= -github.com/markbates/hmax v1.0.0/go.mod h1:cOkR9dktiESxIMu+65oc/r/bdY4bE8zZw3OLhLx0X2c= -github.com/markbates/inflect v1.0.0/go.mod h1:oTeZL2KHA7CUX6X+fovmK9OvIOFuqu0TwdQrZjLTh88= -github.com/markbates/inflect v1.0.1/go.mod h1:uv3UVNBe5qBIfCm8O8Q+DW+S1EopeyINj+Ikhc7rnCk= -github.com/markbates/inflect v1.0.3/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= -github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= -github.com/markbates/oncer v0.0.0-20180924031910-e862a676800b/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/oncer v0.0.0-20180924034138-723ad0170a46/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/oncer v0.0.0-20181014194634-05fccaae8fc4/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/refresh v1.4.10/go.mod h1:NDPHvotuZmTmesXxr95C9bjlw1/0frJwtME2dzcVKhc= -github.com/markbates/safe v1.0.0/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/markbates/sigtx v1.0.0/go.mod h1:QF1Hv6Ic6Ca6W+T+DL0Y/ypborFKyvUY9HmuCD4VeTc= -github.com/markbates/willie v1.0.9/go.mod h1:fsrFVWl91+gXpx/6dv715j7i11fYPfZ9ZGfH0DQzY7w= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= -github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.0.0 h1:o4VLZ5jqHE+HahLT6drNtSGTrrUA3wPBmtpgqtdbClo= -github.com/rogpeppe/go-internal v1.0.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20170515013102-78fb10f4a5f8/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/octicon v0.0.0-20180602230221-c42b0e3b24d9/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= -github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/thrawn01/args v0.3.0 h1:XbMnfGaw6nFbm8hgSncHu20cGrZMTP8BnxiusA43AeE= github.com/thrawn01/args v0.3.0/go.mod h1:TnRiOFjyh7Wa6oC8ACFPc7KIvbzCiluphA3mJUiPIEo= -github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= -github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181024171144-74cb1d3d52f4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181025113841-85e1b3f9139a/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181207154023-610586996380 h1:zPQexyRtNYBc7bcHmehl1dH6TB3qn8zytv8cBGLDNY0= golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180921163948-d47a0f339242/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180927150500-dad3d9fb7b6e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181022134430-8a28ead16f52/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181024145615-5cd93ef61a7c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181025063200-d989b31c8746/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026064943-731415f00dce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181106135930-3a76605856fd/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dzTt5OQ3vMQo9mkOIKIo= golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181003024731-2f84ea8ef872/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181006002542-f60d9635b16a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181013182035-5e66757b835f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181024171208-a2dc47679d30/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181105230042-78dc5bac0cac/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181107215632-34b416bd17b3/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181114190951-94339b83286c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181119130350-139d099f6620/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181127195227-b4e97c0ed882/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181127232545-e782529d0ddd/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181205224935-3576414c54a4/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181206194817-bcd4e47d0288/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/mailgun.go b/mailgun.go index bcc9ba31..d8f5f13e 100644 --- a/mailgun.go +++ b/mailgun.go @@ -1,69 +1,52 @@ -// TODO(sfalvo): -// Document how to run acceptance tests. - -// Package mailgun 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. +// Package mailgun 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 -// Version: 0.99.0 +// Derrick J. Wippler // // 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. +// All functions and method have a corresponding test, so if you don't find an +// example for a function you'd like to know more about, please check 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 +// List iterators // -// 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. +// Most methods that begin with `List` return an iterator which simplfies +// paging through large result sets returned by the mailgun API. Most `List` +// methods allow you to specify a `Limit` parameter which as you'd expect, +// limits the number of items returned per page. Note that, at present, +// Mailgun imposes its own cap of 100 items per page, for all API endpoints. // -// 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. +// For example, the following iterates over all pages of events 100 items at a time // -// Functions that Return Totals +// mg := mailgun.NewMailgun("your-domain.com", "your-api-key") +// it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100}) // -// 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. +// // The entire operation should not take longer than 30 seconds +// ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) +// defer cancel() // -// If this happens, you may find yourself needing to iterate over the dataset of interest. -// For example: +// // For each page of 100 events +// var page []mailgun.Event +// for it.Next(ctx, &page) { +// for _, e := range page { +// // Do something with 'e' +// } +// } // -// // 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. +// Copyright (c) 2013-2019, Michael Banzon. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -100,18 +83,14 @@ import ( "net/http" "os" "time" - - "github.com/gobuffalo/envy" ) +// Set true to write the HTTP requests in curl for to stdout var Debug = false -// ENV is used to help switch settings based on where the -// application is being run. Default is "development". -var ENV = envy.Get("GO_ENV", "development") - const ( - ApiBase = "https://api.mailgun.net/v3" + // Base Url the library uses to contact mailgun. Use SetAPIBase() to override + APIBase = "https://api.mailgun.net/v3" messagesEndpoint = "messages" mimeMessagesEndpoint = "messages.mime" bouncesEndpoint = "bounces" @@ -154,12 +133,12 @@ type Mailgun interface { AddBounce(ctx context.Context, address, code, error string) error DeleteBounce(ctx context.Context, address string) error - GetStats(ctx context.Context, events []string, opts *ListStatOptions) ([]Stats, error) + GetStats(ctx context.Context, events []string, opts *GetStatOptions) ([]Stats, error) GetTag(ctx context.Context, tag string) (Tag, error) DeleteTag(ctx context.Context, tag string) error ListTags(*ListTagOptions) *TagIterator - ListDomains(ctx context.Context, opts *ListOptions) (int, []Domain, error) + ListDomains(ctx context.Context, opts *ListOptions) *DomainsIterator GetDomain(ctx context.Context, domain string) (Domain, []DNSRecord, []DNSRecord, error) CreateDomain(ctx context.Context, name string, pass string, opts *CreateDomainOptions) error DeleteDomain(ctx context.Context, name string) error @@ -257,7 +236,7 @@ type MailgunImpl struct { // NewMailGun creates a new client instance. func NewMailgun(domain, apiKey string) *MailgunImpl { return &MailgunImpl{ - apiBase: ApiBase, + apiBase: APIBase, domain: domain, apiKey: apiKey, client: http.DefaultClient, @@ -286,7 +265,7 @@ func NewMailgunFromEnv() (*MailgunImpl, error) { return mg, nil } -// ApiBase returns the API Base URL configured for this client. +// APIBase returns the API Base URL configured for this client. func (mg *MailgunImpl) APIBase() string { return mg.apiBase } diff --git a/mailing_lists.go b/mailing_lists.go index f401bb5f..f052778b 100644 --- a/mailing_lists.go +++ b/mailing_lists.go @@ -6,18 +6,22 @@ import ( ) // 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" + // ReadOnly specifies that nobody, including Members, may send messages to + // the mailing list. Messages distributed on such lists come from list + // administrator accounts only. + AccessLevelReadOnly = "readonly" + // Members specifies that only those who subscribe to the mailing list may + // send messages. + AccessLevelMembers = "members" + // Everyone specifies that anyone and everyone may both read and submit + // messages to the mailing list, including non-subscribers. + AccessLevelEveryone = "everyone" ) +// Specify the access of a mailing list member +type AccessLevel string + // A List structure provides information for a mailing list. // // AccessLevel may be one of ReadOnly, Members, or Everyone. @@ -25,7 +29,7 @@ type MailingList struct { Address string `json:"address,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` - AccessLevel string `json:"access_level,omitempty"` + AccessLevel AccessLevel `json:"access_level,omitempty"` CreatedAt RFC2822Time `json:"created_at,omitempty"` MembersCount int `json:"members_count,omitempty"` } @@ -174,7 +178,7 @@ func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype MailingL p.addValue("description", prototype.Description) } if prototype.AccessLevel != "" { - p.addValue("access_level", prototype.AccessLevel) + p.addValue("access_level", string(prototype.AccessLevel)) } response, err := makePostRequest(ctx, r, p) if err != nil { @@ -233,7 +237,7 @@ func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, proto p.addValue("description", prototype.Description) } if prototype.AccessLevel != "" { - p.addValue("access_level", prototype.AccessLevel) + p.addValue("access_level", string(prototype.AccessLevel)) } var l MailingList response, err := makePutRequest(ctx, r, p) diff --git a/mailing_lists_test.go b/mailing_lists_test.go index 636a7c4b..5605fa9f 100644 --- a/mailing_lists_test.go +++ b/mailing_lists_test.go @@ -18,7 +18,7 @@ func TestMailingListMembers(t *testing.T) { Address: address, Name: address, Description: "TestMailingListMembers-related mailing list", - AccessLevel: mailgun.Members, + AccessLevel: mailgun.AccessLevelMembers, }) ensure.Nil(t, err) defer func() { @@ -102,7 +102,7 @@ func TestMailingLists(t *testing.T) { Address: address, Name: "List1", Description: "A list created by an acceptance test.", - AccessLevel: mailgun.Members, + AccessLevel: mailgun.AccessLevelMembers, } var countLists = func() int { diff --git a/members.go b/members.go index 739eb0a7..46852492 100644 --- a/members.go +++ b/members.go @@ -50,9 +50,9 @@ type MemberListIterator struct { err error } +// Used by List methods to specify what list parameters to send to the mailgun API type ListOptions struct { Limit int - Skip int } func (mg *MailgunImpl) ListMembers(address string, opts *ListOptions) *MemberListIterator { diff --git a/messages.go b/messages.go index df7c4ce0..a3242622 100644 --- a/messages.go +++ b/messages.go @@ -427,6 +427,7 @@ func (m *Message) AddDomain(domain string) { m.domain = domain } +// Returned by `Send()` when the `mailgun.Message` struct is incomplete var ErrInvalidMessage = errors.New("message not valid") // Send attempts to queue a message (see Message, NewMessage, and its methods) for delivery. diff --git a/mock.go b/mock.go index 1543ca18..958b8753 100644 --- a/mock.go +++ b/mock.go @@ -14,6 +14,7 @@ import ( "github.com/go-chi/chi" ) +// A mailgun api mock suitable for testing type MockServer struct { srv *httptest.Server @@ -25,6 +26,7 @@ type MockServer struct { events []Event } +// Create a new instance of the mailgun API mock server func NewMockServer() MockServer { ms := MockServer{} @@ -47,10 +49,12 @@ func NewMockServer() MockServer { return ms } +// Stop the server func (ms *MockServer) Stop() { ms.srv.Close() } +// Return the URL used to connect to the mock server func (ms *MockServer) URL() string { return ms.srv.URL + "/v3" } diff --git a/mock_domains.go b/mock_domains.go index a5bab13d..3a77798b 100644 --- a/mock_domains.go +++ b/mock_domains.go @@ -25,7 +25,7 @@ func (ms *MockServer) addDomainRoutes(r chi.Router) { SMTPLogin: "postmaster@mailgun.test", SMTPPassword: "4rtqo4p6rrx9", Wildcard: true, - SpamAction: "disabled", + SpamAction: SpamActionDisabled, State: "active", }, Connection: &DomainConnection{ @@ -97,15 +97,39 @@ func (ms *MockServer) addDomainRoutes(r chi.Router) { r.Get("/domains/{domain}/limits/tag", ms.getTagLimits) } -func (ms *MockServer) listDomains(w http.ResponseWriter, _ *http.Request) { +func (ms *MockServer) listDomains(w http.ResponseWriter, r *http.Request) { var list []Domain for _, domain := range ms.domainList { list = append(list, domain.Domain) } - toJSON(w, domainListResponse{ + skip := stringToInt(r.FormValue("skip")) + limit := stringToInt(r.FormValue("limit")) + if limit == 0 { + limit = 100 + } + + if skip > len(list) { + skip = len(list) + } + + end := limit + skip + if end > len(list) { + end = len(list) + } + + // If we are at the end of the list + if skip == end { + toJSON(w, domainsListResponse{ + TotalCount: len(list), + Items: []Domain{}, + }) + return + } + + toJSON(w, domainsListResponse{ TotalCount: len(list), - Items: list, + Items: list[skip:end], }) } @@ -129,7 +153,7 @@ func (ms *MockServer) createDomain(w http.ResponseWriter, r *http.Request) { SMTPLogin: r.FormValue("smtp_login"), SMTPPassword: r.FormValue("smtp_password"), Wildcard: stringToBool(r.FormValue("wildcard")), - SpamAction: r.FormValue("spam_action"), + SpamAction: SpamAction(r.FormValue("spam_action")), State: "active", }, }) diff --git a/mock_mailing_list.go b/mock_mailing_list.go index 57ce15d3..25421835 100644 --- a/mock_mailing_list.go +++ b/mock_mailing_list.go @@ -132,7 +132,7 @@ func (ms *MockServer) updateMailingList(w http.ResponseWriter, r *http.Request) ms.mailingList[i].MailingList.Description = r.FormValue("description") } if r.FormValue("access_level") != "" { - ms.mailingList[i].MailingList.AccessLevel = r.FormValue("access_level") + ms.mailingList[i].MailingList.AccessLevel = AccessLevel(r.FormValue("access_level")) } toJSON(w, okResp{Message: "Mailing list member has been updated"}) return @@ -149,7 +149,7 @@ func (ms *MockServer) createMailingList(w http.ResponseWriter, r *http.Request) Name: r.FormValue("name"), Address: r.FormValue("address"), Description: r.FormValue("description"), - AccessLevel: r.FormValue("access_level"), + AccessLevel: AccessLevel(r.FormValue("access_level")), }, }) toJSON(w, okResp{Message: "Mailing list has been created"}) diff --git a/parse.go b/parse.go index b819b0ae..97304f78 100644 --- a/parse.go +++ b/parse.go @@ -9,6 +9,7 @@ import ( "github.com/mailru/easyjson" ) +// All events returned by the EventIterator conform to this interface type Event interface { easyjson.Unmarshaler easyjson.Marshaler @@ -20,6 +21,7 @@ type Event interface { SetID(id string) } +// A list of all JSON event types returned by the /events API var EventNames = map[string]func() Event{ "accepted": new_(events.Accepted{}), "clicked": new_(events.Clicked{}), @@ -35,11 +37,6 @@ var EventNames = map[string]func() Event{ "list_uploaded": new_(events.ListUploaded{}), } -// Returns a new event users can populate -func NewEvent(e interface{}) Event { - return new_(e)() -} - // new_ is a universal event "constructor". func new_(e interface{}) func() Event { typ := reflect.TypeOf(e) diff --git a/routes.go b/routes.go index 668bd2ae..c81f537b 100644 --- a/routes.go +++ b/routes.go @@ -29,6 +29,7 @@ type Route struct { } type routesListResponse struct { + // is -1 if Next() or First() have not been called TotalCount int `json:"total_count"` Items []Route `json:"items"` } @@ -67,12 +68,6 @@ func (ri *RoutesIterator) Err() error { return ri.err } -// Return the total number of items as returned by the mailgun API -// Returns -1 if Next() or First() have not been called -func (ri *RoutesIterator) Count() int { - return ri.TotalCount -} - // Returns the current offset of the iterator func (ri *RoutesIterator) Offset() int { return ri.offset diff --git a/routes_test.go b/routes_test.go index c2a66ef5..74892c7b 100644 --- a/routes_test.go +++ b/routes_test.go @@ -18,7 +18,7 @@ func TestRouteCRUD(t *testing.T) { var page []mailgun.Route it.Next(ctx, &page) ensure.Nil(t, it.Err()) - return it.Count() + return it.TotalCount } routeCount := countRoutes() @@ -82,7 +82,7 @@ func TestRoutesIterator(t *testing.T) { // Pages should be different ensure.NotDeepEqual(t, firstPage, secondPage) - ensure.True(t, firstIterator.Count() != 0) + ensure.True(t, firstIterator.TotalCount != 0) // Previous() ensure.True(t, it.First(ctx, &firstPage)) diff --git a/spam_complaints.go b/spam_complaints.go index 81dc922b..36900c21 100644 --- a/spam_complaints.go +++ b/spam_complaints.go @@ -10,8 +10,6 @@ const ( ) // 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"` diff --git a/stats.go b/stats.go index d2439467..ee148701 100644 --- a/stats.go +++ b/stats.go @@ -7,22 +7,26 @@ import ( const iso8601date = "2006-01-02" +// Stats on accepted messages type Accepted struct { Incoming int `json:"incoming"` Outgoing int `json:"outgoing"` Total int `json:"total"` } +// Stats on delivered messages type Delivered struct { Smtp int `json:"smtp"` Http int `json:"http"` Total int `json:"total"` } +// Stats on temporary failures type Temporary struct { Espblock int `json:"espblock"` } +// Stats on permanent failures type Permanent struct { SuppressBounce int `json:"suppress-bounce"` SuppressUnsubscribe int `json:"suppress-unsubscribe"` @@ -32,15 +36,18 @@ type Permanent struct { Total int `json:"total"` } +// Stats on failed messages type Failed struct { Temporary Temporary `json:"temporary"` Permanent Permanent `json:"permanent"` } +// Total stats for messages type Total struct { Total int `json:"total"` } +// Stats as returned by `GetStats()` type Stats struct { Time string `json:"time"` Accepted Accepted `json:"accepted"` @@ -60,15 +67,18 @@ type statsTotalResponse struct { Stats []Stats `json:"stats"` } +// Used by GetStats() to specify the resolution stats are for type Resolution string +// Indicate which resolution a stat response for request is for const ( ResolutionHour = Resolution("hour") ResolutionDay = Resolution("day") ResolutionMonth = Resolution("month") ) -type ListStatOptions struct { +// Options for GetStats() +type GetStatOptions struct { Resolution Resolution Duration string Start time.Time @@ -78,7 +88,7 @@ type ListStatOptions struct { } // Returns total stats for a given domain for the specified time period -func (mg *MailgunImpl) GetStats(ctx context.Context, events []string, opts *ListStatOptions) ([]Stats, error) { +func (mg *MailgunImpl) GetStats(ctx context.Context, events []string, opts *GetStatOptions) ([]Stats, error) { r := newHTTPRequest(generateApiUrl(mg, statsTotalEndpoint)) if opts != nil { diff --git a/template.go b/template.go index 2cac1bc6..25a52b58 100644 --- a/template.go +++ b/template.go @@ -8,6 +8,7 @@ import ( type TemplateEngine string +// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine const ( TemplateEngineMustache = TemplateEngine("mustache") TemplateEngineHandlebars = TemplateEngine("handlebars") @@ -33,15 +34,7 @@ type templateListResp struct { Paging Paging `json:"paging"` } -/*type TemplateOptions struct { - Name string - Description string - Template string - Engine TemplateEngine - Comment string - Active bool -}*/ - +// Create a new template which can be used to attach template versions to func (mg *MailgunImpl) CreateTemplate(ctx context.Context, template *Template) error { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint)) r.setClient(mg.Client()) @@ -74,6 +67,7 @@ func (mg *MailgunImpl) CreateTemplate(ctx context.Context, template *Template) e return nil } +// Get a template given the template id func (mg *MailgunImpl) GetTemplate(ctx context.Context, id string) (Template, error) { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + id) r.setClient(mg.Client()) @@ -88,6 +82,7 @@ func (mg *MailgunImpl) GetTemplate(ctx context.Context, id string) (Template, er return resp.Item, nil } +// Update the name and description of a template func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, template *Template) error { if template.Id == "" { return errors.New("UpdateTemplate() Template.Id cannot be empty") @@ -114,6 +109,7 @@ func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, template *Template) e return nil } +// Delete a template given a template id func (mg *MailgunImpl) DeleteTemplate(ctx context.Context, id string) error { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + id) r.setClient(mg.Client()) @@ -128,6 +124,7 @@ type TemplatesIterator struct { err error } +// List all available templates func (mg *MailgunImpl) ListTemplates(opts *ListOptions) *TemplatesIterator { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint)) r.setClient(mg.Client()) diff --git a/template_test.go b/template_test.go index 308f18a4..6e232b36 100644 --- a/template_test.go +++ b/template_test.go @@ -14,7 +14,6 @@ func TestTemplateCRUD(t *testing.T) { if reason := mailgun.SkipNetworkTest(); reason != "" { t.Skip(reason) } - mailgun.Debug = true mg, err := mailgun.NewMailgunFromEnv() ensure.Nil(t, err) diff --git a/template_versions.go b/template_versions.go index 02530f82..e76c354a 100644 --- a/template_versions.go +++ b/template_versions.go @@ -23,6 +23,7 @@ type templateVersionListResp struct { Paging Paging `json:"paging"` } +// Add a template version to a template func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, templateId string, version *TemplateVersion) error { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateId + "/versions") r.setClient(mg.Client()) @@ -49,6 +50,7 @@ func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, templateId string return nil } +// Get a specific version of a template func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, templateId, versionId string) (TemplateVersion, error) { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateId + "/versions/" + versionId) r.setClient(mg.Client()) @@ -62,6 +64,7 @@ func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, templateId, versi return resp.Item.Version, nil } +// Update the comment and mark a version of a template active func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, templateId string, version *TemplateVersion) error { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateId + "/versions/" + version.Id) r.setClient(mg.Client()) @@ -84,6 +87,7 @@ func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, templateId str return nil } +// Delete a specific version of a template func (mg *MailgunImpl) DeleteTemplateVersion(ctx context.Context, templateId, versionId string) error { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateId + "/versions/" + versionId) r.setClient(mg.Client()) @@ -98,6 +102,7 @@ type TemplateVersionsIterator struct { err error } +// List all the versions of a specific template func (mg *MailgunImpl) ListTemplateVersions(templateId string, opts *ListOptions) *TemplateVersionsIterator { r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateId + "/versions") r.setClient(mg.Client()) diff --git a/version.go b/version.go index 5e919e32..bbcdd349 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,4 @@ package mailgun +// Version of current release const Version = "3.0.0"