You've already forked openaccounting-server
mirror of
https://github.com/openaccounting/oa-server.git
synced 2025-12-09 09:00:42 +13:00
mailgun dep
This commit is contained in:
3
vendor/github.com/mailgun/mailgun-go/v4/.gitignore
generated
vendored
Normal file
3
vendor/github.com/mailgun/mailgun-go/v4/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
.idea/
|
||||
cmd/mailgun/mailgun
|
||||
7
vendor/github.com/mailgun/mailgun-go/v4/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/mailgun/mailgun-go/v4/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
language: go
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
216
vendor/github.com/mailgun/mailgun-go/v4/CHANGELOG
generated
vendored
Normal file
216
vendor/github.com/mailgun/mailgun-go/v4/CHANGELOG
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [4.3.0] - 2020-10-14
|
||||
### Changed
|
||||
* Replaced easyjson with json-iterator when marshalling events
|
||||
* Modified the mailgun.Event interface by removing the Marshaller interface from easyjson.
|
||||
* Fixed failure while testing webhook via mailgun web console
|
||||
|
||||
## [4.2.0] - 2020-09-17
|
||||
### Added
|
||||
* Added ListEventsWithDomain()
|
||||
|
||||
## [4.1.4] - 2020-08-20
|
||||
### Changes
|
||||
* Added Storage to Accepted, Delivered and Failed events
|
||||
|
||||
## [4.1.3] - 2020-06-23
|
||||
### Changes
|
||||
* UpdateTemplateVersion() now including the template html in the payload request
|
||||
|
||||
## [4.1.2] - 2020-06-10
|
||||
### Added
|
||||
* Added DeleteBounceList method
|
||||
|
||||
## [4.1.1] - 2020-06-05
|
||||
### Changed
|
||||
* Nows sets initial tag when creating a new template
|
||||
|
||||
## [4.1.0] - 2020-04-23
|
||||
### Changed
|
||||
* Added EmailVerification.reason is now a []string (Fixes #217)
|
||||
|
||||
## [4.0.1] - 2020-03-10
|
||||
### Added
|
||||
* Added SetTemplateVersion and SetTemplateRenderText methods to Message
|
||||
|
||||
## [4.0.0] - 2020-01-27
|
||||
### Changes
|
||||
* Changed `UserVariables` type from `map[string]interface{}` to `interface{}`
|
||||
to handle truncated user-variable messages in events.
|
||||
### Added
|
||||
* Add support for setting AMP content in messages
|
||||
|
||||
## [3.6.3] - 2019-12-03
|
||||
### Changes
|
||||
* Calls to get stats now use epoch as the time format
|
||||
|
||||
## [3.6.2] - 2019-11-18
|
||||
### Added
|
||||
* Added `AddTemplateVariable()` to make adding variables to templates
|
||||
less confusing and error prone.
|
||||
|
||||
## [3.6.1] - 2019-10-24
|
||||
### Added
|
||||
* Added `VerifyWebhookSignature()` to mailgun interface
|
||||
|
||||
## [3.6.1-rc.3] - 2019-07-16
|
||||
### Added
|
||||
* APIBaseEU and APIBaseUS to help customers change regions
|
||||
* Documented how to change regions in the README
|
||||
|
||||
## [3.6.1-rc.2] - 2019-07-01
|
||||
### Changes
|
||||
* Fix the JSON response for `GetMember()`
|
||||
* Typo in format string in max number of tags error
|
||||
|
||||
## [3.6.0] - 2019-06-26
|
||||
### Added
|
||||
* Added UpdateClickTracking() to modify click tracking for a domain
|
||||
* Added UpdateUnsubscribeTracking() to modify unsubscribe tracking for a domain
|
||||
* Added UpdateOpenTracking() to modify open tracking for a domain
|
||||
|
||||
## [3.5.0] - 2019-05-21
|
||||
### Added
|
||||
* Added notice in README about go dep bug.
|
||||
* Added endpoints for webhooks in mock server
|
||||
### Changes
|
||||
* Change names of some parameters on public methods to make their use clearer.
|
||||
* Changed signature of `GetWebhook()` now returns []string.
|
||||
* Changed signature of `ListWebhooks()` now returns map[string][]string.
|
||||
* Both `GetWebhooks()` and `ListWebhooks()` now handle new and legacy webhooks properly.
|
||||
|
||||
## [3.4.0] - 2019-04-23
|
||||
### Added
|
||||
* Added `Message.SetTemplate()` to allow sending with the body of a template.
|
||||
### Changes
|
||||
* Changed signature of `CreateDomain()` moved password into `CreateDomainOptions`
|
||||
|
||||
## [3.4.0] - 2019-04-23
|
||||
### Added
|
||||
* Added `Message.SetTemplate()` to allow sending with the body of a template.
|
||||
### Changes
|
||||
* Changed signature of `CreateDomain()` moved password into `CreateDomainOptions`
|
||||
|
||||
## [3.3.2] - 2019-03-28
|
||||
### Changes
|
||||
* Uncommented DeliveryStatus.Code and change it to an integer (See #175)
|
||||
* Added UserVariables to all Message events (See #176)
|
||||
|
||||
## [3.3.1] - 2019-03-13
|
||||
### Changes
|
||||
* Updated Template calls to reflect the most recent Template API changes.
|
||||
* GetStoredMessage() now accepts a URL instead of an id
|
||||
* Deprecated GetStoredMessageForURL()
|
||||
* Deprecated GetStoredMessageRawForURL()
|
||||
* Fixed GetUnsubscribed()
|
||||
|
||||
### Added
|
||||
* Added `GetStoredAttachment()`
|
||||
|
||||
### Removed
|
||||
* Method `DeleteStoredMessage()` mailgun API no long allows this call
|
||||
|
||||
## [3.3.0] - 2019-01-28
|
||||
### Changes
|
||||
* Changed signature of CreateDomain() Now returns JSON response
|
||||
* Changed signature of GetDomain() Now returns a single DomainResponse
|
||||
* Clarified installation notes for non golang module users
|
||||
* Changed 'Public Key' to 'Public Validation Key' in readme
|
||||
* Fixed issue with Next() for limit/skip based iterators
|
||||
|
||||
### Added
|
||||
* Added VerifyDomain()
|
||||
|
||||
## [3.2.0] - 2019-01-21
|
||||
### Changes
|
||||
* Deprecated mg.VerifyWebhookRequest()
|
||||
|
||||
### Added
|
||||
* Added mailgun.ParseEvent()
|
||||
* Added mailgun.ParseEvents()
|
||||
* Added mg.VerifyWebhookSignature()
|
||||
|
||||
|
||||
## [3.1.0] - 2019-01-16
|
||||
### Changes
|
||||
* Removed context.Context from ListDomains() signature
|
||||
* ListEventOptions.Begin and End are no longer pointers to time.Time
|
||||
|
||||
### Added
|
||||
* Added mg.ReSend() to public Mailgun interface
|
||||
* Added Message.SetSkipVerification()
|
||||
* Added Message.SetRequireTLS()
|
||||
|
||||
## [3.0.0] - 2019-01-15
|
||||
### Added
|
||||
* Added CHANGELOG
|
||||
* Added `AddDomainIP()`
|
||||
* Added `ListDomainIPS()`
|
||||
* Added `DeleteDomainIP()`
|
||||
* Added `ListIPS()`
|
||||
* Added `GetIP()`
|
||||
* Added `GetDomainTracking()`
|
||||
* Added `GetDomainConnection()`
|
||||
* Added `UpdateDomainConnection()`
|
||||
* Added `CreateExport()`
|
||||
* Added `ListExports()`
|
||||
* Added `GetExports()`
|
||||
* Added `GetExportLink()`
|
||||
* Added `CreateTemplate()`
|
||||
* Added `GetTemplate()`
|
||||
* Added `UpdateTemplate()`
|
||||
* Added `DeleteTemplate()`
|
||||
* Added `ListTemplates()`
|
||||
* Added `AddTemplateVersion()`
|
||||
* Added `GetTemplateVersion()`
|
||||
* Added `UpdateTemplateVersion()`
|
||||
* Added `DeleteTemplateVersion()`
|
||||
* Added `ListTemplateVersions()`
|
||||
|
||||
### Changed
|
||||
* Added a `mailgun.MockServer` which duplicates part of the mailgun API; suitable for testing
|
||||
* `ListMailingLists()` now uses the `/pages` API and returns an iterator
|
||||
* `ListMembers()` now uses the `/pages` API and returns an iterator
|
||||
* Renamed public interface methods to be consistent. IE: `GetThing(), ListThing(), CreateThing()`
|
||||
* Moved event objects into the `mailgun/events` package, so names like
|
||||
`MailingList` returned by API calls and `MailingList` as an event object
|
||||
don't conflict and confuse users.
|
||||
* Now using context.Context for all network operations
|
||||
* Test suite will run without MG_ env vars defined
|
||||
* ListRoutes() now uses the iterator interface
|
||||
* Added SkipNetworkTest()
|
||||
* Renamed GetStatsTotals() to GetStats()
|
||||
* Renamed GetUnsubscribes to ListUnsubscribes()
|
||||
* Renamed Unsubscribe() to CreateUnsubscribe()
|
||||
* Renamed RemoveUnsubscribe() to DeleteUnsubscribe()
|
||||
* GetStats() now takes an `*opt` argument to pass optional parameters
|
||||
* Modified GetUnsubscribe() to follow the API
|
||||
* Now using golang modules
|
||||
* ListCredentials() now returns an iterator
|
||||
* ListUnsubscribes() now returns an paging iterator
|
||||
* CreateDomain now accepts CreateDomainOption{}
|
||||
* CreateDomain() now supports all optional parameters not just spam_action and wildcard.
|
||||
* ListComplaints() now returns a page iterator
|
||||
* Renamed `TagItem` to `Tag`
|
||||
* ListBounces() now returns a page iterator
|
||||
* API responses with CreatedAt fields are now unmarshalled into RFC2822
|
||||
* DomainList() now returns an iterator
|
||||
* Updated godoc documentation
|
||||
* Renamed ApiBase to APIBase
|
||||
* Updated copyright to 2019
|
||||
* `ListEvents()` now returns a list of typed events
|
||||
|
||||
### Removed
|
||||
* Removed more deprecated types
|
||||
* Removed gobuffalo/envy dependency
|
||||
* Remove mention of the CLI in the README
|
||||
* Removed mailgun cli from project
|
||||
* Removed GetCode() from `Bounce` struct. Verified API returns 'string' and not 'int'
|
||||
* Removed deprecated methods NewMessage and NewMIMEMessage
|
||||
* Removed ginkgo and gomega tests
|
||||
* Removed GetStats() As the /stats endpoint is depreciated
|
||||
27
vendor/github.com/mailgun/mailgun-go/v4/LICENSE
generated
vendored
Normal file
27
vendor/github.com/mailgun/mailgun-go/v4/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2013-2016, 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.
|
||||
15
vendor/github.com/mailgun/mailgun-go/v4/Makefile
generated
vendored
Normal file
15
vendor/github.com/mailgun/mailgun-go/v4/Makefile
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
.PHONY: all
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
PACKAGE := github.com/mailgun/mailgun-go
|
||||
|
||||
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
|
||||
363
vendor/github.com/mailgun/mailgun-go/v4/README.md
generated
vendored
Normal file
363
vendor/github.com/mailgun/mailgun-go/v4/README.md
generated
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
# Mailgun with Go
|
||||
|
||||
[](https://godoc.org/github.com/mailgun/mailgun-go)
|
||||
[](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).
|
||||
|
||||
## Usage
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
)
|
||||
|
||||
// Your available domain names can be found here:
|
||||
// (https://app.mailgun.com/app/domains)
|
||||
var yourDomain string = "your-domain-name" // e.g. mg.yourcompany.com
|
||||
|
||||
// You can find the Private API Key in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
var privateAPIKey string = "your-private-key"
|
||||
|
||||
|
||||
func main() {
|
||||
// Create an instance of the Mailgun Client
|
||||
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
|
||||
|
||||
sender := "sender@example.com"
|
||||
subject := "Fancy subject!"
|
||||
body := "Hello from Mailgun Go!"
|
||||
recipient := "recipient@example.com"
|
||||
|
||||
// The message object allows you to add attachments and Bcc recipients
|
||||
message := mg.NewMessage(sender, subject, body, recipient)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
// Send the message with a 10 second timeout
|
||||
resp, id, err := mg.Send(ctx, message)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("ID: %s Resp: %s\n", id, resp)
|
||||
}
|
||||
```
|
||||
|
||||
## Get Events
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// You can find the Private API Key in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
|
||||
|
||||
it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
|
||||
|
||||
var page []mailgun.Event
|
||||
|
||||
// The entire operation should not take longer than 30 seconds
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
// For each page of 100 events
|
||||
for it.Next(ctx, &page) {
|
||||
for _, e := range page {
|
||||
// You can access some fields via the interface
|
||||
fmt.Printf("Event: '%s' TimeStamp: '%s'\n", e.GetName(), e.GetTimestamp())
|
||||
|
||||
// and you can act upon each event by type
|
||||
switch event := e.(type) {
|
||||
case *events.Accepted:
|
||||
fmt.Printf("Accepted: auth: %t\n", event.Flags.IsAuthenticated)
|
||||
case *events.Delivered:
|
||||
fmt.Printf("Delivered transport: %s\n", event.Envelope.Transport)
|
||||
case *events.Failed:
|
||||
fmt.Printf("Failed reason: %s\n", event.Reason)
|
||||
case *events.Clicked:
|
||||
fmt.Printf("Clicked GeoLocation: %s\n", event.GeoLocation.Country)
|
||||
case *events.Opened:
|
||||
fmt.Printf("Opened GeoLocation: %s\n", event.GeoLocation.Country)
|
||||
case *events.Rejected:
|
||||
fmt.Printf("Rejected reason: %s\n", event.Reject.Reason)
|
||||
case *events.Stored:
|
||||
fmt.Printf("Stored URL: %s\n", event.Storage.URL)
|
||||
case *events.Unsubscribed:
|
||||
fmt.Printf("Unsubscribed client OS: %s\n", event.ClientInfo.ClientOS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Event Polling
|
||||
The mailgun library has built-in support for polling the events api
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// You can find the Private API Key in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
|
||||
|
||||
begin := time.Now().Add(time.Second * -3)
|
||||
|
||||
// Very short poll interval
|
||||
it := mg.PollEvents(&mailgun.ListEventOptions{
|
||||
// Only events with a timestamp after this date/time will be returned
|
||||
Begin: &begin,
|
||||
// How often we poll the api for new events
|
||||
PollInterval: time.Second * 30,
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Poll until our email event arrives
|
||||
var page []mailgun.Event
|
||||
for it.Poll(ctx, &page) {
|
||||
for _, e := range page {
|
||||
// Do something with event
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Email Validations
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
)
|
||||
|
||||
// If your plan does not include email validations but you have an account,
|
||||
// use your Public Validation api key. If your plan does include email validations,
|
||||
// use your Private API key. You can find both the Private and
|
||||
// Public Validation API Keys in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
var apiKey string = "your-api-key"
|
||||
|
||||
func main() {
|
||||
// To use the /v4 version of validations define MG_URL in the environment
|
||||
// as `https://api.mailgun.net/v4` or set `v.SetAPIBase("https://api.mailgun.net/v4")`
|
||||
|
||||
// Create an instance of the Validator
|
||||
v := mailgun.NewEmailValidator(apiKey)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
email, err := v.ValidateEmail(ctx, "recipient@example.com", false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Valid: %t\n", email.IsValid)
|
||||
}
|
||||
```
|
||||
|
||||
## Webhook Handling
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// You can find the Private API Key in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var payload mailgun.WebhookPayload
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
fmt.Printf("decode JSON error: %s", err)
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
return
|
||||
}
|
||||
|
||||
verified, err := mg.VerifyWebhookSignature(payload.Signature)
|
||||
if err != nil {
|
||||
fmt.Printf("verify error: %s\n", err)
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
return
|
||||
}
|
||||
|
||||
if !verified {
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
fmt.Printf("failed verification %+v\n", payload.Signature)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Verified Signature\n")
|
||||
|
||||
// Parse the event provided by the webhook payload
|
||||
e, err := mailgun.ParseEvent(payload.EventData)
|
||||
if err != nil {
|
||||
fmt.Printf("parse event error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch event := e.(type) {
|
||||
case *events.Accepted:
|
||||
fmt.Printf("Accepted: auth: %t\n", event.Flags.IsAuthenticated)
|
||||
case *events.Delivered:
|
||||
fmt.Printf("Delivered transport: %s\n", event.Envelope.Transport)
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println("Serve on :9090...")
|
||||
if err := http.ListenAndServe(":9090", nil); err != nil {
|
||||
fmt.Printf("serve error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using Templates
|
||||
|
||||
Templates enable you to create message templates on your Mailgun account and then populate the data variables at send-time. This allows you to have your layout and design managed on the server and handle the data on the client. The template variables are added as a JSON stringified `X-Mailgun-Variables` header. For example, if you have a template to send a password reset link, you could do the following:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
)
|
||||
|
||||
// Your available domain names can be found here:
|
||||
// (https://app.mailgun.com/app/domains)
|
||||
var yourDomain string = "your-domain-name" // e.g. mg.yourcompany.com
|
||||
|
||||
// You can find the Private API Key in your Account Menu, under "Settings":
|
||||
// (https://app.mailgun.com/app/account/security)
|
||||
var privateAPIKey string = "your-private-key"
|
||||
|
||||
|
||||
func main() {
|
||||
// Create an instance of the Mailgun Client
|
||||
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
|
||||
|
||||
sender := "sender@example.com"
|
||||
subject := "Fancy subject!"
|
||||
body := ""
|
||||
recipient := "recipient@example.com"
|
||||
|
||||
// The message object allows you to add attachments and Bcc recipients
|
||||
message := mg.NewMessage(sender, subject, body, recipient)
|
||||
message.SetTemplate("passwordReset")
|
||||
message.AddTemplateVariable("passwordResetLink", "some link to your site unique to your user")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
// Send the message with a 10 second timeout
|
||||
resp, id, err := mg.Send(ctx, message)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("ID: %s Resp: %s\n", id, resp)
|
||||
}
|
||||
```
|
||||
|
||||
The official mailgun documentation includes examples using this library. Go
|
||||
[here](https://documentation.mailgun.com/en/latest/api_reference.html#api-reference)
|
||||
and click on the "Go" button at the top of the page.
|
||||
|
||||
### EU Region
|
||||
European customers will need to change the default API Base to access your domains
|
||||
|
||||
```go
|
||||
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
|
||||
mg.SetAPIBase(mailgun.APIBaseEU)
|
||||
```
|
||||
## Installation
|
||||
|
||||
If you are using [golang modules](https://github.com/golang/go/wiki/Modules) make sure you
|
||||
include the `/v4` at the end of your import paths
|
||||
```bash
|
||||
$ go get github.com/mailgun/mailgun-go/v4
|
||||
```
|
||||
|
||||
If you are **not** using golang modules, you can drop the `/v4` at the end of the import path.
|
||||
As long as you are using the latest 1.10 or 1.11 golang release, import paths that end in `/v4`
|
||||
in your code should work fine even if you do not have golang modules enabled for your project.
|
||||
```bash
|
||||
$ go get github.com/mailgun/mailgun-go
|
||||
```
|
||||
|
||||
**NOTE for go dep users**
|
||||
|
||||
Using version 3 of the mailgun-go library with go dep currently results in the following error
|
||||
```
|
||||
"github.com/mailgun/mailgun-go/v4/events", which contains malformed code: no package exists at ...
|
||||
```
|
||||
This is a known bug in go dep. You can follow the PR to fix this bug [here](https://github.com/golang/dep/pull/1963)
|
||||
Until this bug is fixed, the best way to use version 3 of the mailgun-go library is to use the golang community
|
||||
supported [golang modules](https://github.com/golang/go/wiki/Modules).
|
||||
|
||||
## Testing
|
||||
|
||||
*WARNING* - running the tests will cost you money!
|
||||
|
||||
To run the tests various environment variables must be set. These are:
|
||||
|
||||
* `MG_DOMAIN` is the domain name - this is a value registered in the Mailgun admin interface.
|
||||
* `MG_PUBLIC_API_KEY` is the Public Validation API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/app/account/security)
|
||||
* `MG_API_KEY` is the Private API key - you can get this value from the Mailgun [security page](https://app.mailgun.com/app/account/security)
|
||||
* `MG_EMAIL_TO` is the email address used in various sending tests.
|
||||
|
||||
and finally
|
||||
|
||||
* `MG_SPEND_MONEY` if this value is set the part of the test that use the API to actually send email will be run - be aware *this will count on your quota* and *this _will_ cost you money*.
|
||||
|
||||
The code is released under a 3-clause BSD license. See the LICENSE file for more information.
|
||||
211
vendor/github.com/mailgun/mailgun-go/v4/bounces.go
generated
vendored
Normal file
211
vendor/github.com/mailgun/mailgun-go/v4/bounces.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Bounce aggregates data relating to undeliverable messages to a specific intended recipient,
|
||||
// identified by Address.
|
||||
type Bounce struct {
|
||||
// The time at which Mailgun detected the bounce.
|
||||
CreatedAt RFC2822Time `json:"created_at"`
|
||||
// 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 {
|
||||
First string `json:"first,omitempty"`
|
||||
Next string `json:"next,omitempty"`
|
||||
Previous string `json:"previous,omitempty"`
|
||||
Last string `json:"last,omitempty"`
|
||||
}
|
||||
|
||||
type bouncesListResponse struct {
|
||||
Items []Bounce `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
// ListBounces 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 (mg *MailgunImpl) ListBounces(opts *ListOptions) *BouncesIterator {
|
||||
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &BouncesIterator{
|
||||
mg: mg,
|
||||
bouncesListResponse: bouncesListResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type BouncesIterator struct {
|
||||
bouncesListResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ci *BouncesIterator) Err() error {
|
||||
return ci.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Next)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Bounce, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.First)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Bounce, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Last)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Bounce, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
if ci.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Previous)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Bounce, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ci *BouncesIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(ci.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, ci.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &ci.bouncesListResponse)
|
||||
}
|
||||
|
||||
// GetBounce retrieves a single bounce record, if any exist, for the given recipient address.
|
||||
func (mg *MailgunImpl) GetBounce(ctx context.Context, address string) (Bounce, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint) + "/" + address)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var response Bounce
|
||||
err := getResponseFromJSON(ctx, r, &response)
|
||||
return response, 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 (mg *MailgunImpl) AddBounce(ctx context.Context, address, code, error string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("address", address)
|
||||
if code != "" {
|
||||
payload.addValue("code", code)
|
||||
}
|
||||
if error != "" {
|
||||
payload.addValue("error", error)
|
||||
}
|
||||
_, err := makePostRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteBounce removes all bounces associted with the provided e-mail address.
|
||||
func (mg *MailgunImpl) DeleteBounce(ctx context.Context, address string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint) + "/" + address)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteBounceList removes all bounces in the bounce list
|
||||
func (mg *MailgunImpl) DeleteBounceList(ctx context.Context) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, bouncesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
216
vendor/github.com/mailgun/mailgun-go/v4/credentials.go
generated
vendored
Normal file
216
vendor/github.com/mailgun/mailgun-go/v4/credentials.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Credential structure describes a principle allowed to send or receive mail at the domain.
|
||||
type Credential struct {
|
||||
CreatedAt RFC2822Time `json:"created_at"`
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type credentialsListResponse struct {
|
||||
// is -1 if Next() or First() have not been called
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Credential `json:"items"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (mg *MailgunImpl) ListCredentials(opts *ListOptions) *CredentialsIterator {
|
||||
var limit int
|
||||
if opts != nil {
|
||||
limit = opts.Limit
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
return &CredentialsIterator{
|
||||
mg: mg,
|
||||
url: generateCredentialsUrl(mg, ""),
|
||||
credentialsListResponse: credentialsListResponse{TotalCount: -1},
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
type CredentialsIterator struct {
|
||||
credentialsListResponse
|
||||
|
||||
limit int
|
||||
mg Mailgun
|
||||
offset int
|
||||
url string
|
||||
err error
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ri *CredentialsIterator) Err() error {
|
||||
return ri.err
|
||||
}
|
||||
|
||||
// Offset returns the current offset of the iterator
|
||||
func (ri *CredentialsIterator) Offset() int {
|
||||
return ri.offset
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.err = ri.fetch(ctx, ri.offset, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cpy := make([]Credential, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
if len(ri.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
ri.offset = ri.offset + len(ri.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
ri.err = ri.fetch(ctx, 0, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Credential, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
ri.offset = len(ri.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.TotalCount - 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([]Credential, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]Credential) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.offset - (ri.limit * 2)
|
||||
if ri.offset < 0 {
|
||||
ri.offset = 0
|
||||
}
|
||||
|
||||
ri.err = ri.fetch(ctx, ri.offset, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Credential, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
if len(ri.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ri *CredentialsIterator) 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.credentialsListResponse)
|
||||
}
|
||||
|
||||
// CreateCredential attempts to create associate a new principle with your domain.
|
||||
func (mg *MailgunImpl) CreateCredential(ctx context.Context, login, password string) error {
|
||||
if (login == "") || (password == "") {
|
||||
return ErrEmptyParam
|
||||
}
|
||||
r := newHTTPRequest(generateCredentialsUrl(mg, ""))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("login", login)
|
||||
p.addValue("password", password)
|
||||
_, err := makePostRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeCredentialPassword attempts to alter the indicated credential's password.
|
||||
func (mg *MailgunImpl) ChangeCredentialPassword(ctx context.Context, login, password string) error {
|
||||
if (login == "") || (password == "") {
|
||||
return ErrEmptyParam
|
||||
}
|
||||
r := newHTTPRequest(generateCredentialsUrl(mg, login))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("password", password)
|
||||
_, err := makePutRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCredential attempts to remove the indicated principle from the domain.
|
||||
func (mg *MailgunImpl) DeleteCredential(ctx context.Context, login string) error {
|
||||
if login == "" {
|
||||
return ErrEmptyParam
|
||||
}
|
||||
r := newHTTPRequest(generateCredentialsUrl(mg, login))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
386
vendor/github.com/mailgun/mailgun-go/v4/domains.go
generated
vendored
Normal file
386
vendor/github.com/mailgun/mailgun-go/v4/domains.go
generated
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Use these to specify a spam action when creating a new domain.
|
||||
const (
|
||||
// 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")
|
||||
// instructs Mailgun to just block or delete the message all-together.
|
||||
SpamActionDelete = SpamAction("delete")
|
||||
)
|
||||
|
||||
type SpamAction string
|
||||
|
||||
// A Domain structure holds information about a domain used when sending mail.
|
||||
type Domain struct {
|
||||
CreatedAt RFC2822Time `json:"created_at"`
|
||||
SMTPLogin string `json:"smtp_login"`
|
||||
Name string `json:"name"`
|
||||
SMTPPassword string `json:"smtp_password"`
|
||||
Wildcard bool `json:"wildcard"`
|
||||
SpamAction SpamAction `json:"spam_action"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
// 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 DomainResponse struct {
|
||||
Domain Domain `json:"domain"`
|
||||
ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"`
|
||||
SendingDNSRecords []DNSRecord `json:"sending_dns_records"`
|
||||
}
|
||||
|
||||
type domainConnectionResponse struct {
|
||||
Connection DomainConnection `json:"connection"`
|
||||
}
|
||||
|
||||
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"`
|
||||
TextFooter string `json:"text_footer"`
|
||||
}
|
||||
|
||||
type domainTrackingResponse struct {
|
||||
Tracking DomainTracking `json:"tracking"`
|
||||
}
|
||||
|
||||
// ListDomains retrieves a set of domains from Mailgun.
|
||||
func (mg *MailgunImpl) ListDomains(opts *ListOptions) *DomainsIterator {
|
||||
var limit int
|
||||
if opts != nil {
|
||||
limit = opts.Limit
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Offset returns the current offset of the iterator
|
||||
func (ri *DomainsIterator) Offset() int {
|
||||
return ri.offset
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (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 = ri.offset + len(ri.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (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
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.TotalCount - 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
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.offset - (ri.limit * 2)
|
||||
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)
|
||||
}
|
||||
|
||||
// GetDomain retrieves detailed information about the named domain.
|
||||
func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string) (DomainResponse, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp DomainResponse
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (string, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/verify")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
var resp DomainResponse
|
||||
err := putResponseFromJSON(ctx, r, payload, &resp)
|
||||
return resp.Domain.State, err
|
||||
}
|
||||
|
||||
// Optional parameters when creating a domain
|
||||
type CreateDomainOptions struct {
|
||||
Password string
|
||||
SpamAction SpamAction
|
||||
Wildcard bool
|
||||
ForceDKIMAuthority bool
|
||||
DKIMKeySize int
|
||||
IPS []string
|
||||
}
|
||||
|
||||
// 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 (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (DomainResponse, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("name", name)
|
||||
|
||||
if opts != nil {
|
||||
if opts.SpamAction != "" {
|
||||
payload.addValue("spam_action", string(opts.SpamAction))
|
||||
}
|
||||
if opts.Wildcard {
|
||||
payload.addValue("wildcard", boolToString(opts.Wildcard))
|
||||
}
|
||||
if opts.ForceDKIMAuthority {
|
||||
payload.addValue("force_dkim_authority", boolToString(opts.ForceDKIMAuthority))
|
||||
}
|
||||
if opts.DKIMKeySize != 0 {
|
||||
payload.addValue("dkim_key_size", strconv.Itoa(opts.DKIMKeySize))
|
||||
}
|
||||
if len(opts.IPS) != 0 {
|
||||
payload.addValue("ips", strings.Join(opts.IPS, ","))
|
||||
}
|
||||
if len(opts.Password) != 0 {
|
||||
payload.addValue("smtp_password", opts.Password)
|
||||
}
|
||||
}
|
||||
var resp DomainResponse
|
||||
err := postResponseFromJSON(ctx, r, payload, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetDomainConnection returns delivery connection settings for the defined domain
|
||||
func (mg *MailgunImpl) GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/connection")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp domainConnectionResponse
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp.Connection, err
|
||||
}
|
||||
|
||||
// Updates the specified delivery connection settings for the defined domain
|
||||
func (mg *MailgunImpl) UpdateDomainConnection(ctx context.Context, domain string, settings DomainConnection) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/connection")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("require_tls", boolToString(settings.RequireTLS))
|
||||
payload.addValue("skip_verification", boolToString(settings.SkipVerification))
|
||||
_, err := makePutRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteDomain instructs Mailgun to dispose of the named domain name
|
||||
func (mg *MailgunImpl) DeleteDomain(ctx context.Context, name string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + name)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDomainTracking returns tracking settings for a domain
|
||||
func (mg *MailgunImpl) GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/tracking")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp domainTrackingResponse
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp.Tracking, err
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) UpdateClickTracking(ctx context.Context, domain, active string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/tracking/click")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("active", active)
|
||||
_, err := makePutRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) UpdateUnsubscribeTracking(ctx context.Context, domain, active, htmlFooter, textFooter string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/tracking/unsubscribe")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("active", active)
|
||||
payload.addValue("html_footer", htmlFooter)
|
||||
payload.addValue("text_footer", textFooter)
|
||||
_, err := makePutRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) UpdateOpenTracking(ctx context.Context, domain, active string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/tracking/open")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("active", active)
|
||||
_, err := makePutRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
func boolToString(b bool) string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
215
vendor/github.com/mailgun/mailgun-go/v4/email_validation.go
generated
vendored
Normal file
215
vendor/github.com/mailgun/mailgun-go/v4/email_validation.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
type EmailVerification struct {
|
||||
// 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"`
|
||||
// The reason why a specific validation may be unsuccessful. (Available in the V3 response)
|
||||
Reason string `json:"reason"`
|
||||
// A list of potential reasons why a specific validation may be unsuccessful. (Available in the v4 response)
|
||||
Reasons []string
|
||||
}
|
||||
|
||||
type v4EmailValidationResp 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"`
|
||||
}
|
||||
|
||||
type addressParseResult struct {
|
||||
Parsed []string `json:"parsed"`
|
||||
Unparseable []string `json:"unparseable"`
|
||||
}
|
||||
|
||||
type EmailValidator interface {
|
||||
ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (EmailVerification, error)
|
||||
ParseAddresses(ctx context.Context, addresses ...string) ([]string, []string, error)
|
||||
}
|
||||
|
||||
type EmailValidatorImpl struct {
|
||||
client *http.Client
|
||||
isPublicKey bool
|
||||
apiBase string
|
||||
apiKey string
|
||||
}
|
||||
|
||||
// Creates a new validation instance.
|
||||
// * If a public key is provided, uses the public validation endpoints
|
||||
// * If a private key is provided, uses the private validation endpoints
|
||||
func NewEmailValidator(apiKey string) *EmailValidatorImpl {
|
||||
isPublicKey := false
|
||||
|
||||
// Did the user pass in a public key?
|
||||
if strings.HasPrefix(apiKey, "pubkey-") {
|
||||
isPublicKey = true
|
||||
}
|
||||
|
||||
return &EmailValidatorImpl{
|
||||
client: http.DefaultClient,
|
||||
isPublicKey: isPublicKey,
|
||||
apiBase: APIBase,
|
||||
apiKey: apiKey,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEmailValidatorFromEnv returns a new EmailValidator using environment variables
|
||||
// If MG_PUBLIC_API_KEY is set, assume using the free validation subject to daily usage limits
|
||||
// If only MG_API_KEY is set, assume using the /private validation routes with no daily usage limits
|
||||
func NewEmailValidatorFromEnv() (*EmailValidatorImpl, error) {
|
||||
apiKey := os.Getenv("MG_PUBLIC_API_KEY")
|
||||
if apiKey == "" {
|
||||
apiKey = os.Getenv("MG_API_KEY")
|
||||
if apiKey == "" {
|
||||
return nil, errors.New(
|
||||
"environment variable MG_PUBLIC_API_KEY or MG_API_KEY required for email validation")
|
||||
}
|
||||
}
|
||||
v := NewEmailValidator(apiKey)
|
||||
url := os.Getenv("MG_URL")
|
||||
if url != "" {
|
||||
v.SetAPIBase(url)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// APIBase returns the API Base URL configured for this client.
|
||||
func (m *EmailValidatorImpl) APIBase() string {
|
||||
return m.apiBase
|
||||
}
|
||||
|
||||
// SetAPIBase updates the API Base URL for this client.
|
||||
func (m *EmailValidatorImpl) SetAPIBase(address string) {
|
||||
m.apiBase = address
|
||||
}
|
||||
|
||||
// SetClient updates the HTTP client for this client.
|
||||
func (m *EmailValidatorImpl) SetClient(c *http.Client) {
|
||||
m.client = c
|
||||
}
|
||||
|
||||
// Client returns the HTTP client configured for this client.
|
||||
func (m *EmailValidatorImpl) Client() *http.Client {
|
||||
return m.client
|
||||
}
|
||||
|
||||
// APIKey returns the API key used for validations
|
||||
func (m *EmailValidatorImpl) APIKey() string {
|
||||
return m.apiKey
|
||||
}
|
||||
|
||||
func (m *EmailValidatorImpl) getAddressURL(endpoint string) string {
|
||||
if m.isPublicKey {
|
||||
return fmt.Sprintf("%s/address/%s", m.APIBase(), endpoint)
|
||||
}
|
||||
return fmt.Sprintf("%s/address/private/%s", m.APIBase(), endpoint)
|
||||
}
|
||||
|
||||
// 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. If user has set the
|
||||
func (m *EmailValidatorImpl) ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (EmailVerification, error) {
|
||||
if strings.HasSuffix(m.APIBase(), "/v4") {
|
||||
return m.validateV4(ctx, email, mailBoxVerify)
|
||||
}
|
||||
return m.validateV3(ctx, email, mailBoxVerify)
|
||||
}
|
||||
|
||||
func (m *EmailValidatorImpl) validateV3(ctx context.Context, email string, mailBoxVerify bool) (EmailVerification, error) {
|
||||
r := newHTTPRequest(m.getAddressURL("validate"))
|
||||
r.setClient(m.Client())
|
||||
r.addParameter("address", email)
|
||||
if mailBoxVerify {
|
||||
r.addParameter("mailbox_verification", "true")
|
||||
}
|
||||
r.setBasicAuth(basicAuthUser, m.APIKey())
|
||||
|
||||
var res EmailVerification
|
||||
err := getResponseFromJSON(ctx, r, &res)
|
||||
if err != nil {
|
||||
return EmailVerification{}, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *EmailValidatorImpl) validateV4(ctx context.Context, email string, mailBoxVerify bool) (EmailVerification, error) {
|
||||
r := newHTTPRequest(fmt.Sprintf("%s/address/validate", m.APIBase()))
|
||||
r.setClient(m.Client())
|
||||
r.addParameter("address", email)
|
||||
if mailBoxVerify {
|
||||
r.addParameter("mailbox_verification", "true")
|
||||
}
|
||||
r.setBasicAuth(basicAuthUser, m.APIKey())
|
||||
|
||||
var res v4EmailValidationResp
|
||||
err := getResponseFromJSON(ctx, r, &res)
|
||||
if err != nil {
|
||||
return EmailVerification{}, err
|
||||
}
|
||||
return EmailVerification{
|
||||
IsValid: res.IsValid,
|
||||
MailboxVerification: res.MailboxVerification,
|
||||
Parts: res.Parts,
|
||||
Address: res.Address,
|
||||
DidYouMean: res.DidYouMean,
|
||||
IsDisposableAddress: res.IsDisposableAddress,
|
||||
IsRoleAddress: res.IsRoleAddress,
|
||||
Reasons: res.Reason}, 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 *EmailValidatorImpl) ParseAddresses(ctx context.Context, addresses ...string) ([]string, []string, error) {
|
||||
r := newHTTPRequest(m.getAddressURL("parse"))
|
||||
r.setClient(m.Client())
|
||||
r.addParameter("addresses", strings.Join(addresses, ","))
|
||||
r.setBasicAuth(basicAuthUser, m.APIKey())
|
||||
|
||||
var response addressParseResult
|
||||
err := getResponseFromJSON(ctx, r, &response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.Parsed, response.Unparseable, nil
|
||||
}
|
||||
293
vendor/github.com/mailgun/mailgun-go/v4/events.go
generated
vendored
Normal file
293
vendor/github.com/mailgun/mailgun-go/v4/events.go
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
// ListEventOptions{} modifies the behavior of ListEvents()
|
||||
type ListEventOptions struct {
|
||||
// Limits the results to a specific start and end time
|
||||
Begin, End time.Time
|
||||
// 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.
|
||||
ForceAscending, ForceDescending bool
|
||||
// Compact, if true, compacts the returned JSON to minimize transmission bandwidth.
|
||||
Compact bool
|
||||
// Limit caps the number of results returned. If left unspecified, MailGun assumes 100.
|
||||
Limit int
|
||||
// Filter allows the caller to provide more specialized filters on the query.
|
||||
// Consult the Mailgun documentation for more details.
|
||||
Filter map[string]string
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// EventIterator maintains the state necessary for paging though small parcels of a larger set of events.
|
||||
type EventIterator struct {
|
||||
events.Response
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// Create an new iterator to fetch a page of events from the events api with a specific domain
|
||||
func (mg *MailgunImpl) ListEventsWithDomain(opts *ListEventOptions, domain string) *EventIterator {
|
||||
url := generateApiUrlWithDomain(mg, eventsEndpoint, domain)
|
||||
return mg.listEvents(url, opts)
|
||||
}
|
||||
|
||||
// Create an new iterator to fetch a page of events from the events api
|
||||
func (mg *MailgunImpl) ListEvents(opts *ListEventOptions) *EventIterator {
|
||||
url := generateApiUrl(mg, eventsEndpoint)
|
||||
return mg.listEvents(url, opts)
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) listEvents(url string, opts *ListEventOptions) *EventIterator {
|
||||
req := newHTTPRequest(url)
|
||||
if opts != nil {
|
||||
if opts.Limit > 0 {
|
||||
req.addParameter("limit", fmt.Sprintf("%d", opts.Limit))
|
||||
}
|
||||
if opts.Compact {
|
||||
req.addParameter("pretty", "no")
|
||||
}
|
||||
if opts.ForceAscending {
|
||||
req.addParameter("ascending", "yes")
|
||||
} else if opts.ForceDescending {
|
||||
req.addParameter("ascending", "no")
|
||||
}
|
||||
if !opts.Begin.IsZero() {
|
||||
req.addParameter("begin", formatMailgunTime(opts.Begin))
|
||||
}
|
||||
if !opts.End.IsZero() {
|
||||
req.addParameter("end", formatMailgunTime(opts.End))
|
||||
}
|
||||
if opts.Filter != nil {
|
||||
for k, v := range opts.Filter {
|
||||
req.addParameter(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
url, err := req.generateUrlWithParameters()
|
||||
return &EventIterator{
|
||||
mg: mg,
|
||||
Response: events.Response{Paging: events.Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ei *EventIterator) Err() error {
|
||||
return ei.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of events 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 (ei *EventIterator) Next(ctx context.Context, events *[]Event) bool {
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
ei.err = ei.fetch(ctx, ei.Paging.Next)
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
*events, ei.err = ParseEvents(ei.Items)
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
if len(ei.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of events 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 (ei *EventIterator) First(ctx context.Context, events *[]Event) bool {
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
ei.err = ei.fetch(ctx, ei.Paging.First)
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
*events, ei.err = ParseEvents(ei.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of events 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 (ei *EventIterator) Last(ctx context.Context, events *[]Event) bool {
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
ei.err = ei.fetch(ctx, ei.Paging.Last)
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
*events, ei.err = ParseEvents(ei.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of events 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 (ei *EventIterator) Previous(ctx context.Context, events *[]Event) bool {
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
if ei.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
ei.err = ei.fetch(ctx, ei.Paging.Previous)
|
||||
if ei.err != nil {
|
||||
return false
|
||||
}
|
||||
*events, ei.err = ParseEvents(ei.Items)
|
||||
if len(ei.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ei *EventIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(ei.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, ei.mg.APIKey())
|
||||
|
||||
resp, err := makeRequest(ctx, r, "GET", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := jsoniter.Unmarshal(resp.Data, &ei.Response); err != nil {
|
||||
return fmt.Errorf("failed to un-marshall event.Response: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EventPoller maintains the state necessary for polling events
|
||||
type EventPoller struct {
|
||||
it *EventIterator
|
||||
opts ListEventOptions
|
||||
thresholdTime time.Time
|
||||
beginTime time.Time
|
||||
sleepUntil time.Time
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// 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
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
//
|
||||
// // 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
|
||||
opts.ForceAscending = true
|
||||
|
||||
// Default begin time is 30 minutes ago
|
||||
if opts.Begin.IsZero() {
|
||||
opts.Begin = now.Add(time.Minute * -30)
|
||||
}
|
||||
|
||||
// Set a 15 second poll interval if none set
|
||||
if opts.PollInterval.Nanoseconds() == 0 {
|
||||
opts.PollInterval = time.Duration(time.Second * 15)
|
||||
}
|
||||
|
||||
return &EventPoller{
|
||||
it: mg.ListEvents(opts),
|
||||
opts: *opts,
|
||||
mg: mg,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during polling `Err()` will return non nil
|
||||
func (ep *EventPoller) Err() error {
|
||||
return ep.err
|
||||
}
|
||||
|
||||
func (ep *EventPoller) Poll(ctx context.Context, events *[]Event) bool {
|
||||
var currentPage string
|
||||
var results []Event
|
||||
|
||||
if ep.opts.Begin.IsZero() {
|
||||
ep.beginTime = time.Now().UTC()
|
||||
}
|
||||
|
||||
for {
|
||||
// Remember our current page url
|
||||
currentPage = ep.it.Paging.Next
|
||||
|
||||
// Attempt to get a page of events
|
||||
var page []Event
|
||||
if ep.it.Next(ctx, &page) == false {
|
||||
if ep.it.Err() == nil && len(page) == 0 {
|
||||
// No events, sleep for our poll interval
|
||||
goto SLEEP
|
||||
}
|
||||
ep.err = ep.it.Err()
|
||||
return false
|
||||
}
|
||||
|
||||
for _, e := range page {
|
||||
// If any events on the page are older than our being time
|
||||
if e.GetTimestamp().After(ep.beginTime) {
|
||||
results = append(results, e)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have events to return
|
||||
if len(results) != 0 {
|
||||
*events = results
|
||||
results = nil
|
||||
return true
|
||||
}
|
||||
|
||||
SLEEP:
|
||||
// Since we didn't find an event older than our
|
||||
// threshold, fetch this same page again
|
||||
ep.it.Paging.Next = currentPage
|
||||
|
||||
// Sleep the rest of our duration
|
||||
tick := time.NewTicker(ep.opts.PollInterval)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-tick.C:
|
||||
tick.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Given time.Time{} return a float64 as given in mailgun event timestamps
|
||||
func TimeToFloat(t time.Time) float64 {
|
||||
return float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000))
|
||||
}
|
||||
57
vendor/github.com/mailgun/mailgun-go/v4/events/enums.go
generated
vendored
Normal file
57
vendor/github.com/mailgun/mailgun-go/v4/events/enums.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package events
|
||||
|
||||
const (
|
||||
EventAccepted = "accepted"
|
||||
EventRejected = "rejected"
|
||||
EventDelivered = "delivered"
|
||||
EventFailed = "failed"
|
||||
EventOpened = "opened"
|
||||
EventClicked = "clicked"
|
||||
EventUnsubscribed = "unsubscribed"
|
||||
EventComplained = "complained"
|
||||
EventStored = "stored"
|
||||
EventDropped = "dropped"
|
||||
EventListMemberUploaded = "list_member_uploaded"
|
||||
EventListMemberUploadError = "list_member_upload_error"
|
||||
EventListUploaded = "list_uploaded"
|
||||
)
|
||||
|
||||
const (
|
||||
TransportHTTP = "http"
|
||||
TransportSMTP = "smtp"
|
||||
|
||||
DeviceUnknown = "unknown"
|
||||
DeviceMobileBrowser = "desktop"
|
||||
DeviceBrowser = "mobile"
|
||||
DeviceEmail = "tablet"
|
||||
DeviceOther = "other"
|
||||
|
||||
ClientUnknown = "unknown"
|
||||
ClientMobileBrowser = "mobile browser"
|
||||
ClientBrowser = "browser"
|
||||
ClientEmail = "email client"
|
||||
ClientLibrary = "library"
|
||||
ClientRobot = "robot"
|
||||
ClientOther = "other"
|
||||
|
||||
ReasonUnknown = "unknown"
|
||||
ReasonGeneric = "generic"
|
||||
ReasonBounce = "bounce"
|
||||
ReasonESPBlock = "espblock"
|
||||
ReasonGreylisted = "greylisted"
|
||||
ReasonBlacklisted = "blacklisted"
|
||||
ReasonSuppressBounce = "suppress-bounce"
|
||||
ReasonSuppressComplaint = "suppress-complaint"
|
||||
ReasonSuppressUnsubscribe = "suppress-unsubscribe"
|
||||
ReasonOld = "old"
|
||||
ReasonHardFail = "hardfail"
|
||||
|
||||
SeverityUnknown = "unknown"
|
||||
SeverityTemporary = "temporary"
|
||||
SeverityPermanent = "permanent"
|
||||
SeverityInternal = "internal"
|
||||
|
||||
MethodUnknown = "unknown"
|
||||
MethodSMTP = "smtp"
|
||||
MethodHTTP = "http"
|
||||
)
|
||||
265
vendor/github.com/mailgun/mailgun-go/v4/events/events.go
generated
vendored
Normal file
265
vendor/github.com/mailgun/mailgun-go/v4/events/events.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An EventName is a struct with the event name.
|
||||
type EventName struct {
|
||||
Name string `json:"event"`
|
||||
}
|
||||
|
||||
// GetName returns the name of the event.
|
||||
func (e *EventName) GetName() string {
|
||||
return strings.ToLower(e.Name)
|
||||
}
|
||||
|
||||
func (e *EventName) SetName(name string) {
|
||||
e.Name = strings.ToLower(name)
|
||||
}
|
||||
|
||||
type Generic struct {
|
||||
EventName
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (g *Generic) GetTimestamp() time.Time {
|
||||
return time.Unix(0, int64(g.Timestamp*float64(time.Second))).UTC()
|
||||
}
|
||||
|
||||
func (g *Generic) SetTimestamp(t time.Time) {
|
||||
// convert := fmt.Sprintf("%d.%06d", t.Unix(), t.Nanosecond()/int(time.Microsecond))
|
||||
// ts, err := strconv.ParseFloat(convert, 64)
|
||||
g.Timestamp = float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000))
|
||||
}
|
||||
|
||||
func (g *Generic) GetID() string {
|
||||
return g.ID
|
||||
}
|
||||
|
||||
func (g *Generic) SetID(id string) {
|
||||
g.ID = id
|
||||
}
|
||||
|
||||
//
|
||||
// Message Events
|
||||
//
|
||||
|
||||
type Accepted struct {
|
||||
Generic
|
||||
|
||||
Envelope Envelope `json:"envelope"`
|
||||
Message Message `json:"message"`
|
||||
Flags Flags `json:"flags"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Method string `json:"method"`
|
||||
OriginatingIP string `json:"originating-ip"`
|
||||
Tags []string `json:"tags"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
Storage Storage `json:"storage"`
|
||||
}
|
||||
|
||||
type Rejected struct {
|
||||
Generic
|
||||
|
||||
Reject struct {
|
||||
Reason string `json:"reason"`
|
||||
Description string `json:"description"`
|
||||
} `json:"reject"`
|
||||
|
||||
Message Message `json:"message"`
|
||||
Storage Storage `json:"storage"`
|
||||
Flags Flags `json:"flags"`
|
||||
|
||||
Tags []string `json:"tags"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Delivered struct {
|
||||
Generic
|
||||
|
||||
Envelope Envelope `json:"envelope"`
|
||||
Message Message `json:"message"`
|
||||
Flags Flags `json:"flags"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Method string `json:"method"`
|
||||
Tags []string `json:"tags"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
Storage Storage `json:"storage"`
|
||||
|
||||
DeliveryStatus DeliveryStatus `json:"delivery-status"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Failed struct {
|
||||
Generic
|
||||
|
||||
Envelope Envelope `json:"envelope"`
|
||||
Message Message `json:"message"`
|
||||
Flags Flags `json:"flags"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Method string `json:"method"`
|
||||
Tags []string `json:"tags"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
Storage Storage `json:"storage"`
|
||||
|
||||
DeliveryStatus DeliveryStatus `json:"delivery-status"`
|
||||
Severity string `json:"severity"`
|
||||
Reason string `json:"reason"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Stored struct {
|
||||
Generic
|
||||
|
||||
Message Message `json:"message"`
|
||||
Storage Storage `json:"storage"`
|
||||
Flags Flags `json:"flags"`
|
||||
|
||||
Tags []string `json:"tags"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
//
|
||||
// Message Events (User)
|
||||
//
|
||||
|
||||
type Opened struct {
|
||||
Generic
|
||||
|
||||
Message Message `json:"message"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
IP string `json:"ip"`
|
||||
ClientInfo ClientInfo `json:"client-info"`
|
||||
GeoLocation GeoLocation `json:"geolocation"`
|
||||
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Clicked struct {
|
||||
Generic
|
||||
|
||||
Url string `json:"url"`
|
||||
|
||||
Message Message `json:"message"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
IP string `json:"ip"`
|
||||
ClientInfo ClientInfo `json:"client-info"`
|
||||
GeoLocation GeoLocation `json:"geolocation"`
|
||||
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Unsubscribed struct {
|
||||
Generic
|
||||
|
||||
Message Message `json:"message"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
RecipientDomain string `json:"recipient-domain"`
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
IP string `json:"ip"`
|
||||
ClientInfo ClientInfo `json:"client-info"`
|
||||
GeoLocation GeoLocation `json:"geolocation"`
|
||||
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
type Complained struct {
|
||||
Generic
|
||||
|
||||
Message Message `json:"message"`
|
||||
Campaigns []Campaign `json:"campaigns"`
|
||||
|
||||
Recipient string `json:"recipient"`
|
||||
Tags []string `json:"tags"`
|
||||
UserVariables interface{} `json:"user-variables"`
|
||||
}
|
||||
|
||||
//
|
||||
// Mailing List Events
|
||||
//
|
||||
|
||||
type MailingListMember struct {
|
||||
Subscribed bool
|
||||
Address string
|
||||
Name string
|
||||
Vars []string
|
||||
}
|
||||
|
||||
type MailingListError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
type ListMemberUploaded struct {
|
||||
Generic
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
Member MailingListMember `json:"member"`
|
||||
TaskID string `json:"task-id"`
|
||||
}
|
||||
|
||||
type ListMemberUploadError struct {
|
||||
Generic
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
TaskID string `json:"task-id"`
|
||||
Format string `json:"format"`
|
||||
MemberDescription string `json:"member-description"`
|
||||
Error MailingListError `json:"error"`
|
||||
}
|
||||
|
||||
type ListUploaded struct {
|
||||
Generic
|
||||
MailingList MailingList `json:"mailing-list"`
|
||||
IsUpsert bool `json:"is-upsert"`
|
||||
Format string `json:"format"`
|
||||
UpsertedCount int `json:"upserted-count"`
|
||||
FailedCount int `json:"failed-count"`
|
||||
Member MailingListMember `json:"member"`
|
||||
Subscribed bool `json:"subscribed"`
|
||||
TaskID string `json:"task-id"`
|
||||
}
|
||||
|
||||
type Paging struct {
|
||||
First string `json:"first,omitempty"`
|
||||
Next string `json:"next,omitempty"`
|
||||
Previous string `json:"previous,omitempty"`
|
||||
Last string `json:"last,omitempty"`
|
||||
}
|
||||
|
||||
type RawJSON []byte
|
||||
|
||||
func (v *RawJSON) UnmarshalJSON(data []byte) error {
|
||||
*v = data
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Items []RawJSON `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
78
vendor/github.com/mailgun/mailgun-go/v4/events/objects.go
generated
vendored
Normal file
78
vendor/github.com/mailgun/mailgun-go/v4/events/objects.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package events
|
||||
|
||||
type ClientInfo struct {
|
||||
AcceptLanguage string `json:"accept-language"`
|
||||
ClientName string `json:"client-name"`
|
||||
ClientOS string `json:"client-os"`
|
||||
ClientType string `json:"client-type"`
|
||||
DeviceType string `json:"device-type"`
|
||||
IP string `json:"ip"`
|
||||
UserAgent string `json:"user-agent"`
|
||||
}
|
||||
|
||||
type GeoLocation struct {
|
||||
City string `json:"city"`
|
||||
Country string `json:"country"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type MailingList struct {
|
||||
Address string `json:"address"`
|
||||
ListID string `json:"list-id"`
|
||||
SID string `json:"sid"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Headers MessageHeaders `json:"headers"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type Envelope struct {
|
||||
MailFrom string `json:"mail-from"`
|
||||
Sender string `json:"sender"`
|
||||
Transport string `json:"transport"`
|
||||
Targets string `json:"targets"`
|
||||
SendingHost string `json:"sending-host"`
|
||||
SendingIP string `json:"sending-ip"`
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
Key string `json:"key"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type Flags struct {
|
||||
IsAuthenticated bool `json:"is-authenticated"`
|
||||
IsBig bool `json:"is-big"`
|
||||
IsSystemTest bool `json:"is-system-test"`
|
||||
IsTestMode bool `json:"is-test-mode"`
|
||||
IsDelayedBounce bool `json:"is-delayed-bounce"`
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
FileName string `json:"filename"`
|
||||
ContentType string `json:"content-type"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type MessageHeaders struct {
|
||||
To string `json:"to"`
|
||||
MessageID string `json:"message-id"`
|
||||
From string `json:"from"`
|
||||
Subject string `json:"subject"`
|
||||
}
|
||||
|
||||
type Campaign struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type DeliveryStatus struct {
|
||||
Code int `json:"code"`
|
||||
AttemptNo int `json:"attempt-no"`
|
||||
Description string `json:"description"`
|
||||
Message string `json:"message"`
|
||||
SessionSeconds float64 `json:"session-seconds"`
|
||||
}
|
||||
99
vendor/github.com/mailgun/mailgun-go/v4/exports.go
generated
vendored
Normal file
99
vendor/github.com/mailgun/mailgun-go/v4/exports.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ExportList struct {
|
||||
Items []Export `json:"items"`
|
||||
}
|
||||
|
||||
type Export struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// Create an export based on the URL given
|
||||
func (mg *MailgunImpl) CreateExport(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, exportsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("url", url)
|
||||
_, err := makePostRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// List all exports created within the past 24 hours
|
||||
func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]Export, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, exportsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
if url != "" {
|
||||
r.addParameter("url", url)
|
||||
}
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var resp ExportList
|
||||
if err := getResponseFromJSON(ctx, r, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []Export
|
||||
for _, item := range resp.Items {
|
||||
result = append(result, Export(item))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetExport gets an export by id
|
||||
func (mg *MailgunImpl) GetExport(ctx context.Context, id string) (Export, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, exportsEndpoint) + "/" + id)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp Export
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Download an export by ID. This will respond with a '302 Moved'
|
||||
// with the Location header of temporary S3 URL if it is available.
|
||||
func (mg *MailgunImpl) GetExportLink(ctx context.Context, id string) (string, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, exportsEndpoint) + "/" + id + "/download_url")
|
||||
c := mg.Client()
|
||||
|
||||
// Ensure the client doesn't attempt to retry
|
||||
c.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return errors.New("redirect")
|
||||
}
|
||||
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
|
||||
req, err := r.NewRequest(ctx, "GET", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if Debug {
|
||||
fmt.Println(r.curlString(req, nil))
|
||||
}
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode == http.StatusFound {
|
||||
url, err := resp.Location()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("while parsing 302 redirect url: %s", err)
|
||||
}
|
||||
return url.String(), nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf("expected a 302 response, API returned a '%d' instead", resp.StatusCode)
|
||||
}
|
||||
12
vendor/github.com/mailgun/mailgun-go/v4/go.mod
generated
vendored
Normal file
12
vendor/github.com/mailgun/mailgun-go/v4/go.mod
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/mailgun/mailgun-go/v4
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/go-chi/chi v4.0.0+incompatible
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/pkg/errors v0.8.1
|
||||
)
|
||||
23
vendor/github.com/mailgun/mailgun-go/v4/go.sum
generated
vendored
Normal file
23
vendor/github.com/mailgun/mailgun-go/v4/go.sum
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
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/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/go-chi/chi v4.0.0+incompatible h1:SiLLEDyAkqNnw+T/uDTf3aFB9T4FTrwMpuYrgaRcnW4=
|
||||
github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
330
vendor/github.com/mailgun/mailgun-go/v4/httphelpers.go
generated
vendored
Normal file
330
vendor/github.com/mailgun/mailgun-go/v4/httphelpers.go
generated
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var validURL = regexp.MustCompile(`^/v[2-4].*`)
|
||||
|
||||
type httpRequest struct {
|
||||
URL string
|
||||
Parameters map[string][]string
|
||||
Headers map[string]string
|
||||
BasicAuthUser string
|
||||
BasicAuthPassword string
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
type httpResponse struct {
|
||||
Code int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type payload interface {
|
||||
getPayloadBuffer() (*bytes.Buffer, error)
|
||||
getContentType() string
|
||||
getValues() []keyValuePair
|
||||
}
|
||||
|
||||
type keyValuePair struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
type keyNameRC struct {
|
||||
key string
|
||||
name string
|
||||
value io.ReadCloser
|
||||
}
|
||||
|
||||
type keyNameBuff struct {
|
||||
key string
|
||||
name string
|
||||
value []byte
|
||||
}
|
||||
|
||||
type formDataPayload struct {
|
||||
contentType string
|
||||
Values []keyValuePair
|
||||
Files []keyValuePair
|
||||
ReadClosers []keyNameRC
|
||||
Buffers []keyNameBuff
|
||||
}
|
||||
|
||||
type urlEncodedPayload struct {
|
||||
Values []keyValuePair
|
||||
}
|
||||
|
||||
func newHTTPRequest(url string) *httpRequest {
|
||||
return &httpRequest{URL: url, Client: http.DefaultClient}
|
||||
}
|
||||
|
||||
func (r *httpRequest) addParameter(name, value string) {
|
||||
if r.Parameters == nil {
|
||||
r.Parameters = make(map[string][]string)
|
||||
}
|
||||
r.Parameters[name] = append(r.Parameters[name], value)
|
||||
}
|
||||
|
||||
func (r *httpRequest) setClient(c *http.Client) {
|
||||
r.Client = c
|
||||
}
|
||||
|
||||
func (r *httpRequest) setBasicAuth(user, password string) {
|
||||
r.BasicAuthUser = user
|
||||
r.BasicAuthPassword = password
|
||||
}
|
||||
|
||||
func newUrlEncodedPayload() *urlEncodedPayload {
|
||||
return &urlEncodedPayload{}
|
||||
}
|
||||
|
||||
func (f *urlEncodedPayload) addValue(key, value string) {
|
||||
f.Values = append(f.Values, keyValuePair{key: key, value: value})
|
||||
}
|
||||
|
||||
func (f *urlEncodedPayload) getPayloadBuffer() (*bytes.Buffer, error) {
|
||||
data := url.Values{}
|
||||
for _, keyVal := range f.Values {
|
||||
data.Add(keyVal.key, keyVal.value)
|
||||
}
|
||||
return bytes.NewBufferString(data.Encode()), nil
|
||||
}
|
||||
|
||||
func (f *urlEncodedPayload) getContentType() string {
|
||||
return "application/x-www-form-urlencoded"
|
||||
}
|
||||
|
||||
func (f *urlEncodedPayload) getValues() []keyValuePair {
|
||||
return f.Values
|
||||
}
|
||||
|
||||
func (r *httpResponse) parseFromJSON(v interface{}) error {
|
||||
return json.Unmarshal(r.Data, v)
|
||||
}
|
||||
|
||||
func newFormDataPayload() *formDataPayload {
|
||||
return &formDataPayload{}
|
||||
}
|
||||
|
||||
func (f *formDataPayload) getValues() []keyValuePair {
|
||||
return f.Values
|
||||
}
|
||||
|
||||
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) addBuffer(key, file string, buff []byte) {
|
||||
f.Buffers = append(f.Buffers, keyNameBuff{key: key, name: file, value: buff})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
for _, buff := range f.Buffers {
|
||||
if tmp, err := writer.CreateFormFile(buff.key, buff.name); err == nil {
|
||||
r := bytes.NewReader(buff.value)
|
||||
io.Copy(tmp, r)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
f.contentType = writer.FormDataContentType()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (f *formDataPayload) getContentType() string {
|
||||
if f.contentType == "" {
|
||||
f.getPayloadBuffer()
|
||||
}
|
||||
return f.contentType
|
||||
}
|
||||
|
||||
func (r *httpRequest) addHeader(name, value string) {
|
||||
if r.Headers == nil {
|
||||
r.Headers = make(map[string]string)
|
||||
}
|
||||
r.Headers[name] = value
|
||||
}
|
||||
|
||||
func (r *httpRequest) makeGetRequest(ctx context.Context) (*httpResponse, error) {
|
||||
return r.makeRequest(ctx, "GET", nil)
|
||||
}
|
||||
|
||||
func (r *httpRequest) makePostRequest(ctx context.Context, payload payload) (*httpResponse, error) {
|
||||
return r.makeRequest(ctx, "POST", payload)
|
||||
}
|
||||
|
||||
func (r *httpRequest) makePutRequest(ctx context.Context, payload payload) (*httpResponse, error) {
|
||||
return r.makeRequest(ctx, "PUT", payload)
|
||||
}
|
||||
|
||||
func (r *httpRequest) makeDeleteRequest(ctx context.Context) (*httpResponse, error) {
|
||||
return r.makeRequest(ctx, "DELETE", nil)
|
||||
}
|
||||
|
||||
func (r *httpRequest) NewRequest(ctx context.Context, method string, payload payload) (*http.Request, 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
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
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)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (r *httpRequest) makeRequest(ctx context.Context, method string, payload payload) (*httpResponse, error) {
|
||||
req, err := r.NewRequest(ctx, method, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
fmt.Println(r.curlString(req, payload))
|
||||
}
|
||||
|
||||
response := httpResponse{}
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if resp != nil {
|
||||
response.Code = resp.StatusCode
|
||||
}
|
||||
if err != nil {
|
||||
if urlErr, ok := err.(*url.Error); ok {
|
||||
if urlErr.Err == io.EOF {
|
||||
return nil, errors.Wrap(err, "remote server prematurely closed connection")
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrap(err, "while making http request")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while reading response body")
|
||||
}
|
||||
|
||||
response.Data = responseBody
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (r *httpRequest) generateUrlWithParameters() (string, error) {
|
||||
url, err := url.Parse(r.URL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !validURL.MatchString(url.Path) {
|
||||
return "", errors.New(`BaseAPI must end with a /v2, /v3 or /v4; setBaseAPI("https://host/v3")`)
|
||||
}
|
||||
|
||||
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) curlString(req *http.Request, p payload) string {
|
||||
|
||||
parts := []string{"curl", "-i", "-X", req.Method, req.URL.String()}
|
||||
for key, value := range req.Header {
|
||||
parts = append(parts, fmt.Sprintf("-H \"%s: %s\"", key, value[0]))
|
||||
}
|
||||
|
||||
//parts = append(parts, fmt.Sprintf(" --user '%s:%s'", r.BasicAuthUser, r.BasicAuthPassword))
|
||||
|
||||
if p != nil {
|
||||
for _, param := range p.getValues() {
|
||||
parts = append(parts, fmt.Sprintf(" -F %s='%s'", param.key, param.value))
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
87
vendor/github.com/mailgun/mailgun-go/v4/ips.go
generated
vendored
Normal file
87
vendor/github.com/mailgun/mailgun-go/v4/ips.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package mailgun
|
||||
|
||||
import "context"
|
||||
|
||||
type ipAddressListResponse struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []string `json:"items"`
|
||||
}
|
||||
|
||||
type IPAddress struct {
|
||||
IP string `json:"ip"`
|
||||
RDNS string `json:"rdns"`
|
||||
Dedicated bool `json:"dedicated"`
|
||||
}
|
||||
|
||||
type okResp struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ListIPS returns a list of IPs assigned to your account
|
||||
func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, ipsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
if dedicated {
|
||||
r.addParameter("dedicated", "true")
|
||||
}
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var resp ipAddressListResponse
|
||||
if err := getResponseFromJSON(ctx, r, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []IPAddress
|
||||
for _, ip := range resp.Items {
|
||||
result = append(result, IPAddress{IP: ip})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetIP returns information about the specified IP
|
||||
func (mg *MailgunImpl) GetIP(ctx context.Context, ip string) (IPAddress, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, ipsEndpoint) + "/" + ip)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp IPAddress
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListDomainIPS returns a list of IPs currently assigned to the specified domain.
|
||||
func (mg *MailgunImpl) ListDomainIPS(ctx context.Context) ([]IPAddress, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + mg.domain + "/ips")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var resp ipAddressListResponse
|
||||
if err := getResponseFromJSON(ctx, r, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []IPAddress
|
||||
for _, ip := range resp.Items {
|
||||
result = append(result, IPAddress{IP: ip})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Assign a dedicated IP to the domain specified.
|
||||
func (mg *MailgunImpl) AddDomainIP(ctx context.Context, ip string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + mg.domain + "/ips")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("ip", ip)
|
||||
_, err := makePostRequest(ctx, r, payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unassign an IP from the domain specified.
|
||||
func (mg *MailgunImpl) DeleteDomainIP(ctx context.Context, ip string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + mg.domain + "/ips/" + ip)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
18
vendor/github.com/mailgun/mailgun-go/v4/limits.go
generated
vendored
Normal file
18
vendor/github.com/mailgun/mailgun-go/v4/limits.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package mailgun
|
||||
|
||||
import "context"
|
||||
|
||||
type TagLimits struct {
|
||||
Limit int `json:"limit"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// GetTagLimits returns tracking settings for a domain
|
||||
func (mg *MailgunImpl) GetTagLimits(ctx context.Context, domain string) (TagLimits, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, domainsEndpoint) + "/" + domain + "/limits/tag")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var resp TagLimits
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
394
vendor/github.com/mailgun/mailgun-go/v4/mailgun.go
generated
vendored
Normal file
394
vendor/github.com/mailgun/mailgun-go/v4/mailgun.go
generated
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
// 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 <sam.falvo %at% rackspace.com>
|
||||
// Derrick J. Wippler <thrawn01 %at% gmail.com>
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// List iterators
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// For example, the following iterates over all pages of events 100 items at a time
|
||||
//
|
||||
// mg := mailgun.NewMailgun("your-domain.com", "your-api-key")
|
||||
// it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
|
||||
//
|
||||
// // The entire operation should not take longer than 30 seconds
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
// defer cancel()
|
||||
//
|
||||
// // For each page of 100 events
|
||||
// var page []mailgun.Event
|
||||
// for it.Next(ctx, &page) {
|
||||
// for _, e := range page {
|
||||
// // Do something with 'e'
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// License
|
||||
//
|
||||
// Copyright (c) 2013-2019, 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 (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Set true to write the HTTP requests in curl for to stdout
|
||||
var Debug = false
|
||||
|
||||
const (
|
||||
// Base Url the library uses to contact mailgun. Use SetAPIBase() to override
|
||||
APIBase = "https://api.mailgun.net/v3"
|
||||
APIBaseUS = APIBase
|
||||
APIBaseEU = "https://api.eu.mailgun.net/v3"
|
||||
messagesEndpoint = "messages"
|
||||
mimeMessagesEndpoint = "messages.mime"
|
||||
bouncesEndpoint = "bounces"
|
||||
statsTotalEndpoint = "stats/total"
|
||||
domainsEndpoint = "domains"
|
||||
tagsEndpoint = "tags"
|
||||
eventsEndpoint = "events"
|
||||
unsubscribesEndpoint = "unsubscribes"
|
||||
routesEndpoint = "routes"
|
||||
ipsEndpoint = "ips"
|
||||
exportsEndpoint = "exports"
|
||||
webhooksEndpoint = "webhooks"
|
||||
listsEndpoint = "lists"
|
||||
basicAuthUser = "api"
|
||||
templatesEndpoint = "templates"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
APIBase() string
|
||||
Domain() string
|
||||
APIKey() string
|
||||
Client() *http.Client
|
||||
SetClient(client *http.Client)
|
||||
SetAPIBase(url string)
|
||||
|
||||
Send(ctx context.Context, m *Message) (string, string, error)
|
||||
ReSend(ctx context.Context, id string, recipients ...string) (string, string, error)
|
||||
NewMessage(from, subject, text string, to ...string) *Message
|
||||
NewMIMEMessage(body io.ReadCloser, to ...string) *Message
|
||||
|
||||
ListBounces(opts *ListOptions) *BouncesIterator
|
||||
GetBounce(ctx context.Context, address string) (Bounce, error)
|
||||
AddBounce(ctx context.Context, address, code, err string) error
|
||||
DeleteBounce(ctx context.Context, address string) error
|
||||
DeleteBounceList(ctx context.Context) 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(opts *ListOptions) *DomainsIterator
|
||||
GetDomain(ctx context.Context, domain string) (DomainResponse, error)
|
||||
CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (DomainResponse, error)
|
||||
DeleteDomain(ctx context.Context, name string) error
|
||||
VerifyDomain(ctx context.Context, name string) (string, error)
|
||||
UpdateDomainConnection(ctx context.Context, domain string, dc DomainConnection) error
|
||||
GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error)
|
||||
GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error)
|
||||
UpdateClickTracking(ctx context.Context, domain, active string) error
|
||||
UpdateUnsubscribeTracking(ctx context.Context, domain, active, htmlFooter, textFooter string) error
|
||||
UpdateOpenTracking(ctx context.Context, domain, active string) error
|
||||
|
||||
GetStoredMessage(ctx context.Context, url string) (StoredMessage, error)
|
||||
GetStoredMessageRaw(ctx context.Context, id string) (StoredMessageRaw, error)
|
||||
GetStoredAttachment(ctx context.Context, url string) ([]byte, error)
|
||||
|
||||
// Deprecated
|
||||
GetStoredMessageForURL(ctx context.Context, url string) (StoredMessage, error)
|
||||
// Deprecated
|
||||
GetStoredMessageRawForURL(ctx context.Context, url string) (StoredMessageRaw, error)
|
||||
|
||||
ListCredentials(opts *ListOptions) *CredentialsIterator
|
||||
CreateCredential(ctx context.Context, login, password string) error
|
||||
ChangeCredentialPassword(ctx context.Context, login, password string) error
|
||||
DeleteCredential(ctx context.Context, login string) error
|
||||
|
||||
ListUnsubscribes(opts *ListOptions) *UnsubscribesIterator
|
||||
GetUnsubscribe(ctx context.Context, address string) (Unsubscribe, error)
|
||||
CreateUnsubscribe(ctx context.Context, address, tag string) error
|
||||
DeleteUnsubscribe(ctx context.Context, address string) error
|
||||
DeleteUnsubscribeWithTag(ctx context.Context, a, t string) error
|
||||
|
||||
ListComplaints(opts *ListOptions) *ComplaintsIterator
|
||||
GetComplaint(ctx context.Context, address string) (Complaint, error)
|
||||
CreateComplaint(ctx context.Context, address string) error
|
||||
DeleteComplaint(ctx context.Context, address string) error
|
||||
|
||||
ListRoutes(opts *ListOptions) *RoutesIterator
|
||||
GetRoute(ctx context.Context, address string) (Route, error)
|
||||
CreateRoute(ctx context.Context, address Route) (Route, error)
|
||||
DeleteRoute(ctx context.Context, address string) error
|
||||
UpdateRoute(ctx context.Context, address string, r Route) (Route, error)
|
||||
|
||||
ListWebhooks(ctx context.Context) (map[string][]string, error)
|
||||
CreateWebhook(ctx context.Context, kind string, url []string) error
|
||||
DeleteWebhook(ctx context.Context, kind string) error
|
||||
GetWebhook(ctx context.Context, kind string) ([]string, error)
|
||||
UpdateWebhook(ctx context.Context, kind string, url []string) error
|
||||
VerifyWebhookRequest(req *http.Request) (verified bool, err error)
|
||||
VerifyWebhookSignature(sig Signature) (verified bool, err error)
|
||||
|
||||
ListMailingLists(opts *ListOptions) *ListsIterator
|
||||
CreateMailingList(ctx context.Context, address MailingList) (MailingList, error)
|
||||
DeleteMailingList(ctx context.Context, address string) error
|
||||
GetMailingList(ctx context.Context, address string) (MailingList, error)
|
||||
UpdateMailingList(ctx context.Context, address string, ml MailingList) (MailingList, error)
|
||||
|
||||
ListMembers(address string, opts *ListOptions) *MemberListIterator
|
||||
GetMember(ctx context.Context, MemberAddr, listAddr string) (Member, error)
|
||||
CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error
|
||||
CreateMemberList(ctx context.Context, subscribed *bool, addr string, newMembers []interface{}) error
|
||||
UpdateMember(ctx context.Context, Member, list string, prototype Member) (Member, error)
|
||||
DeleteMember(ctx context.Context, Member, list string) error
|
||||
|
||||
ListEventsWithDomain(opts *ListEventOptions, domain string) *EventIterator
|
||||
ListEvents(*ListEventOptions) *EventIterator
|
||||
PollEvents(*ListEventOptions) *EventPoller
|
||||
|
||||
ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error)
|
||||
GetIP(ctx context.Context, ip string) (IPAddress, error)
|
||||
ListDomainIPS(ctx context.Context) ([]IPAddress, error)
|
||||
AddDomainIP(ctx context.Context, ip string) error
|
||||
DeleteDomainIP(ctx context.Context, ip string) error
|
||||
|
||||
ListExports(ctx context.Context, url string) ([]Export, error)
|
||||
GetExport(ctx context.Context, id string) (Export, error)
|
||||
GetExportLink(ctx context.Context, id string) (string, error)
|
||||
CreateExport(ctx context.Context, url string) error
|
||||
|
||||
GetTagLimits(ctx context.Context, domain string) (TagLimits, error)
|
||||
|
||||
CreateTemplate(ctx context.Context, template *Template) error
|
||||
GetTemplate(ctx context.Context, name string) (Template, error)
|
||||
UpdateTemplate(ctx context.Context, template *Template) error
|
||||
DeleteTemplate(ctx context.Context, name string) error
|
||||
ListTemplates(opts *ListTemplateOptions) *TemplatesIterator
|
||||
|
||||
AddTemplateVersion(ctx context.Context, templateName string, version *TemplateVersion) error
|
||||
GetTemplateVersion(ctx context.Context, templateName, tag string) (TemplateVersion, error)
|
||||
UpdateTemplateVersion(ctx context.Context, templateName string, version *TemplateVersion) error
|
||||
DeleteTemplateVersion(ctx context.Context, templateName, tag string) error
|
||||
ListTemplateVersions(templateName string, opts *ListOptions) *TemplateVersionsIterator
|
||||
}
|
||||
|
||||
// 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 {
|
||||
apiBase string
|
||||
domain string
|
||||
apiKey string
|
||||
client *http.Client
|
||||
baseURL string
|
||||
}
|
||||
|
||||
// NewMailGun creates a new client instance.
|
||||
func NewMailgun(domain, apiKey string) *MailgunImpl {
|
||||
return &MailgunImpl{
|
||||
apiBase: APIBase,
|
||||
domain: domain,
|
||||
apiKey: apiKey,
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMailgunFromEnv returns a new Mailgun client using the environment variables
|
||||
// MG_API_KEY, MG_DOMAIN, and MG_URL
|
||||
func NewMailgunFromEnv() (*MailgunImpl, error) {
|
||||
apiKey := os.Getenv("MG_API_KEY")
|
||||
if apiKey == "" {
|
||||
return nil, errors.New("required environment variable MG_API_KEY not defined")
|
||||
}
|
||||
domain := os.Getenv("MG_DOMAIN")
|
||||
if domain == "" {
|
||||
return nil, errors.New("required environment variable MG_DOMAIN not defined")
|
||||
}
|
||||
|
||||
mg := NewMailgun(domain, apiKey)
|
||||
|
||||
url := os.Getenv("MG_URL")
|
||||
if url != "" {
|
||||
mg.SetAPIBase(url)
|
||||
}
|
||||
|
||||
return mg, nil
|
||||
}
|
||||
|
||||
// APIBase returns the API Base URL configured for this client.
|
||||
func (mg *MailgunImpl) APIBase() string {
|
||||
return mg.apiBase
|
||||
}
|
||||
|
||||
// Domain returns the domain configured for this client.
|
||||
func (mg *MailgunImpl) Domain() string {
|
||||
return mg.domain
|
||||
}
|
||||
|
||||
// ApiKey returns the API key configured for this client.
|
||||
func (mg *MailgunImpl) APIKey() string {
|
||||
return mg.apiKey
|
||||
}
|
||||
|
||||
// Client returns the HTTP client configured for this client.
|
||||
func (mg *MailgunImpl) Client() *http.Client {
|
||||
return mg.client
|
||||
}
|
||||
|
||||
// SetClient updates the HTTP client for this client.
|
||||
func (mg *MailgunImpl) SetClient(c *http.Client) {
|
||||
mg.client = c
|
||||
}
|
||||
|
||||
// SetAPIBase updates the API Base URL for this client.
|
||||
// // For EU Customers
|
||||
// mg.SetAPIBase(mailgun.APIBaseEU)
|
||||
//
|
||||
// // For US Customers
|
||||
// mg.SetAPIBase(mailgun.APIBaseUS)
|
||||
//
|
||||
// // Set a custom base API
|
||||
// mg.SetAPIBase("https://localhost/v3")
|
||||
func (mg *MailgunImpl) SetAPIBase(address string) {
|
||||
mg.apiBase = address
|
||||
}
|
||||
|
||||
// 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", m.APIBase(), m.Domain(), endpoint)
|
||||
}
|
||||
|
||||
// generateApiUrlWithDomain renders a URL for an API endpoint using a separate domain and endpoint name.
|
||||
func generateApiUrlWithDomain(m Mailgun, endpoint, domain string) string {
|
||||
return fmt.Sprintf("%s/%s/%s", m.APIBase(), domain, endpoint)
|
||||
}
|
||||
|
||||
// generateMemberApiUrl renders a URL relevant for specifying mailing list members.
|
||||
// The address parameter refers to the mailing list in question.
|
||||
func generateMemberApiUrl(m Mailgun, endpoint, address string) string {
|
||||
return fmt.Sprintf("%s/%s/%s/members", m.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", m.APIBase(), m.Domain(), endpoint)
|
||||
}
|
||||
|
||||
// generateCredentialsUrl renders a URL as generateDomainApiUrl,
|
||||
// but focuses on the SMTP credentials family of API functions.
|
||||
func generateCredentialsUrl(m Mailgun, login string) string {
|
||||
tail := ""
|
||||
if login != "" {
|
||||
tail = fmt.Sprintf("/%s", login)
|
||||
}
|
||||
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(m Mailgun, endpoint string) string {
|
||||
return fmt.Sprintf("%s/%s", m.APIBase(), endpoint)
|
||||
}
|
||||
|
||||
// generateParameterizedUrl works as generateApiUrl, but supports query parameters.
|
||||
func generateParameterizedUrl(m Mailgun, endpoint string, payload 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")
|
||||
}
|
||||
249
vendor/github.com/mailgun/mailgun-go/v4/mailing_lists.go
generated
vendored
Normal file
249
vendor/github.com/mailgun/mailgun-go/v4/mailing_lists.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A mailing list may have one of three membership modes.
|
||||
const (
|
||||
// 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.
|
||||
type MailingList struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
AccessLevel AccessLevel `json:"access_level,omitempty"`
|
||||
CreatedAt RFC2822Time `json:"created_at,omitempty"`
|
||||
MembersCount int `json:"members_count,omitempty"`
|
||||
}
|
||||
|
||||
type listsResponse struct {
|
||||
Items []MailingList `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
type mailingListResponse struct {
|
||||
MailingList MailingList `json:"member"`
|
||||
}
|
||||
|
||||
type ListsIterator struct {
|
||||
listsResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// ListMailingLists returns the specified set of mailing lists administered by your account.
|
||||
func (mg *MailgunImpl) ListMailingLists(opts *ListOptions) *ListsIterator {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/pages")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &ListsIterator{
|
||||
mg: mg,
|
||||
listsResponse: listsResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (li *ListsIterator) Err() error {
|
||||
return li.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Next)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]MailingList, len(li.Items))
|
||||
copy(cpy, li.Items)
|
||||
*items = cpy
|
||||
if len(li.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.First)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]MailingList, len(li.Items))
|
||||
copy(cpy, li.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Last)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]MailingList, len(li.Items))
|
||||
copy(cpy, li.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
if li.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Previous)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]MailingList, len(li.Items))
|
||||
copy(cpy, li.Items)
|
||||
*items = cpy
|
||||
if len(li.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (li *ListsIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(li.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, li.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &li.listsResponse)
|
||||
}
|
||||
|
||||
// CreateMailingList 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) CreateMailingList(ctx context.Context, prototype MailingList) (MailingList, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := 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", string(prototype.AccessLevel))
|
||||
}
|
||||
response, err := makePostRequest(ctx, r, p)
|
||||
if err != nil {
|
||||
return MailingList{}, err
|
||||
}
|
||||
var l MailingList
|
||||
err = response.parseFromJSON(&l)
|
||||
return l, err
|
||||
}
|
||||
|
||||
// DeleteMailingList 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) DeleteMailingList(ctx context.Context, addr string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetMailingList 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) GetMailingList(ctx context.Context, addr string) (MailingList, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
response, err := makeGetRequest(ctx, r)
|
||||
if err != nil {
|
||||
return MailingList{}, err
|
||||
}
|
||||
|
||||
var resp mailingListResponse
|
||||
err = response.parseFromJSON(&resp)
|
||||
return resp.MailingList, err
|
||||
}
|
||||
|
||||
// UpdateMailingList 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) UpdateMailingList(ctx context.Context, addr string, prototype MailingList) (MailingList, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := 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", string(prototype.AccessLevel))
|
||||
}
|
||||
var l MailingList
|
||||
response, err := makePutRequest(ctx, r, p)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
err = response.parseFromJSON(&l)
|
||||
return l, err
|
||||
}
|
||||
264
vendor/github.com/mailgun/mailgun-go/v4/members.go
generated
vendored
Normal file
264
vendor/github.com/mailgun/mailgun-go/v4/members.go
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// yes and no are variables which provide us the ability to take their addresses.
|
||||
// Subscribed and Unsubscribed are pointers to these booleans.
|
||||
//
|
||||
// We use a pointer to boolean as a kind of trinary data type:
|
||||
// if nil, the relevant data type remains unspecified.
|
||||
// Otherwise, its value is either true or false.
|
||||
var (
|
||||
yes bool = true
|
||||
no bool = false
|
||||
)
|
||||
|
||||
// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
|
||||
// This attribute may be used to filter the results returned by GetSubscribers().
|
||||
// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
|
||||
var (
|
||||
All *bool = nil
|
||||
Subscribed *bool = &yes
|
||||
Unsubscribed *bool = &no
|
||||
)
|
||||
|
||||
// A Member structure represents a member of the mailing list.
|
||||
// The Vars field can represent any JSON-encodable data.
|
||||
type Member struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Subscribed *bool `json:"subscribed,omitempty"`
|
||||
Vars map[string]interface{} `json:"vars,omitempty"`
|
||||
}
|
||||
|
||||
type memberListResponse struct {
|
||||
Lists []Member `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
type memberResponse struct {
|
||||
Member Member `json:"member"`
|
||||
}
|
||||
|
||||
type MemberListIterator struct {
|
||||
memberListResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// Used by List methods to specify what list parameters to send to the mailgun API
|
||||
type ListOptions struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (mg *MailgunImpl) ListMembers(address string, opts *ListOptions) *MemberListIterator {
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, address) + "/pages")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &MemberListIterator{
|
||||
mg: mg,
|
||||
memberListResponse: memberListResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (li *MemberListIterator) Err() error {
|
||||
return li.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Next)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = li.Lists
|
||||
if len(li.Lists) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.First)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = li.Lists
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Last)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = li.Lists
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (li *MemberListIterator) Previous(ctx context.Context, items *[]Member) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
if li.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Previous)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = li.Lists
|
||||
if len(li.Lists) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (li *MemberListIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(li.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, li.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &li.memberListResponse)
|
||||
}
|
||||
|
||||
// GetMember returns a complete Member structure for a member of a mailing list,
|
||||
// given only their subscription e-mail address.
|
||||
func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, error) {
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
response, err := makeGetRequest(ctx, r)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
var resp memberResponse
|
||||
err = response.parseFromJSON(&resp)
|
||||
return resp.Member, err
|
||||
}
|
||||
|
||||
// CreateMember registers a new member of the indicated mailing list.
|
||||
// If merge is set to true, then the registration may update an existing Member's settings.
|
||||
// Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address.
|
||||
func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error {
|
||||
vs, err := json.Marshal(prototype.Vars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newFormDataPayload()
|
||||
p.addValue("upsert", yesNo(merge))
|
||||
p.addValue("address", prototype.Address)
|
||||
p.addValue("name", prototype.Name)
|
||||
p.addValue("vars", string(vs))
|
||||
if prototype.Subscribed != nil {
|
||||
p.addValue("subscribed", yesNo(*prototype.Subscribed))
|
||||
}
|
||||
_, err = makePostRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateMember lets you change certain details about the indicated mailing list member.
|
||||
// Address, Name, Vars, and Subscribed fields may be changed.
|
||||
func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype Member) (Member, error) {
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newFormDataPayload()
|
||||
if prototype.Address != "" {
|
||||
p.addValue("address", prototype.Address)
|
||||
}
|
||||
if prototype.Name != "" {
|
||||
p.addValue("name", prototype.Name)
|
||||
}
|
||||
if prototype.Vars != nil {
|
||||
vs, err := json.Marshal(prototype.Vars)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
p.addValue("vars", string(vs))
|
||||
}
|
||||
if prototype.Subscribed != nil {
|
||||
p.addValue("subscribed", yesNo(*prototype.Subscribed))
|
||||
}
|
||||
response, err := makePutRequest(ctx, r, p)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
var envelope struct {
|
||||
Member Member `json:"member"`
|
||||
}
|
||||
err = response.parseFromJSON(&envelope)
|
||||
return envelope.Member, err
|
||||
}
|
||||
|
||||
// DeleteMember removes the member from the list.
|
||||
func (mg *MailgunImpl) DeleteMember(ctx context.Context, member, addr string) error {
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + "/" + member)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateMemberList registers multiple Members and non-Member members to a single mailing list
|
||||
// in a single round-trip.
|
||||
// u indicates if the existing members should be updated or duplicates should be updated.
|
||||
// Use All to elect not to provide a default.
|
||||
// The newMembers list can take one of two JSON-encodable forms: an slice of strings, or
|
||||
// a slice of Member structures.
|
||||
// If a simple slice of strings is passed, each string refers to the member's e-mail address.
|
||||
// Otherwise, each Member needs to have at least the Address field filled out.
|
||||
// Other fields are optional, but may be set according to your needs.
|
||||
func (mg *MailgunImpl) CreateMemberList(ctx context.Context, u *bool, addr string, newMembers []interface{}) error {
|
||||
r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + ".json")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newFormDataPayload()
|
||||
if u != nil {
|
||||
p.addValue("upsert", yesNo(*u))
|
||||
}
|
||||
bs, err := json.Marshal(newMembers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.addValue("members", string(bs))
|
||||
_, err = makePostRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
839
vendor/github.com/mailgun/mailgun-go/v4/messages.go
generated
vendored
Normal file
839
vendor/github.com/mailgun/mailgun-go/v4/messages.go
generated
vendored
Normal file
@@ -0,0 +1,839 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// MaxNumberOfTags represents the maximum number of tags that can be added for a message
|
||||
const MaxNumberOfTags = 3
|
||||
|
||||
// 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
|
||||
readerInlines []ReaderAttachment
|
||||
bufferAttachments []BufferAttachment
|
||||
|
||||
nativeSend bool
|
||||
testMode bool
|
||||
tracking bool
|
||||
trackingClicks bool
|
||||
trackingOpens bool
|
||||
headers map[string]string
|
||||
variables map[string]string
|
||||
templateVariables map[string]interface{}
|
||||
recipientVariables map[string]map[string]interface{}
|
||||
domain string
|
||||
templateVersionTag string
|
||||
templateRenderText bool
|
||||
|
||||
dkimSet bool
|
||||
trackingSet bool
|
||||
trackingClicksSet bool
|
||||
trackingOpensSet bool
|
||||
requireTLS bool
|
||||
skipVerification bool
|
||||
|
||||
specific features
|
||||
mg Mailgun
|
||||
}
|
||||
|
||||
type ReaderAttachment struct {
|
||||
Filename string
|
||||
ReadCloser io.ReadCloser
|
||||
}
|
||||
|
||||
type BufferAttachment struct {
|
||||
Filename string
|
||||
Buffer []byte
|
||||
}
|
||||
|
||||
// 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]struct {
|
||||
Url string `json:"url"`
|
||||
ContentType string `json:"content-type"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
} `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
|
||||
ampHtml string
|
||||
template 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, setHtml and setAMPHtml are invoked via the package-global AddCC, AddBCC,
|
||||
// RecipientCount, SetHtml and SetAMPHtml 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)
|
||||
setAMPHtml(string)
|
||||
addValues(*formDataPayload)
|
||||
isValid() bool
|
||||
endpoint() string
|
||||
recipientCount() int
|
||||
setTemplate(string)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
// AddBufferAttachment arranges to send a file along with the e-mail message.
|
||||
// File contents are read from the []byte array provided
|
||||
// The filename parameter is the resulting filename of the attachment.
|
||||
// The buffer parameter is the []byte array which contains the actual bytes to be used
|
||||
// as the contents of the attached file.
|
||||
func (m *Message) AddBufferAttachment(filename string, buffer []byte) {
|
||||
ba := BufferAttachment{Filename: filename, Buffer: buffer}
|
||||
m.bufferAttachments = append(m.bufferAttachments, ba)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// AddReaderInline 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) AddReaderInline(filename string, readCloser io.ReadCloser) {
|
||||
ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser}
|
||||
m.readerInlines = append(m.readerInlines, ra)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// It will return an error if the limit of recipients have been exceeded for this message
|
||||
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.
|
||||
// It will return an error if the limit of recipients have been exceeded for this message
|
||||
func (m *Message) AddRecipientAndVariables(r string, vars map[string]interface{}) error {
|
||||
if m.RecipientCount() >= MaxNumberOfRecipients {
|
||||
return fmt.Errorf("recipient limit exceeded (max %d)", MaxNumberOfRecipients)
|
||||
}
|
||||
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(ctx context.Context) (string, string, error) {
|
||||
return m.mg.Send(ctx, m)
|
||||
}
|
||||
|
||||
// SetReplyTo sets the receiver who should receive replies
|
||||
func (m *Message) SetReplyTo(recipient string) {
|
||||
m.AddHeader("Reply-To", recipient)
|
||||
}
|
||||
|
||||
// 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) {}
|
||||
|
||||
// SetHtml is a helper. 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) {}
|
||||
|
||||
// SetAMP is a helper. If you're sending a message that isn't already MIME encoded, SetAMP() will arrange to bundle
|
||||
// an AMP-For-Email representation of your message in addition to your html & plain-text content.
|
||||
func (m *Message) SetAMPHtml(html string) {
|
||||
m.specific.setAMPHtml(html)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) setAMPHtml(h string) {
|
||||
pm.ampHtml = h
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) setAMPHtml(_ string) {}
|
||||
|
||||
// AddTag attaches tags 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) error {
|
||||
if len(m.tags) >= MaxNumberOfTags {
|
||||
return fmt.Errorf("cannot add any new tags. Message tag limit (%d) reached", MaxNumberOfTags)
|
||||
}
|
||||
|
||||
m.tags = append(m.tags, tag...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTemplate sets the name of a template stored via the template API.
|
||||
// See https://documentation.mailgun.com/en/latest/user_manual.html#templating
|
||||
func (m *Message) SetTemplate(t string) {
|
||||
m.specific.setTemplate(t)
|
||||
}
|
||||
|
||||
func (pm *plainMessage) setTemplate(t string) {
|
||||
pm.template = t
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) setTemplate(t string) {}
|
||||
|
||||
// AddCampaign is no longer supported and 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
|
||||
}
|
||||
|
||||
// EnableNativeSend allows the return path to match the address in the Message.Headers.From:
|
||||
// field when sending from Mailgun rather than the usual bounce+ address in the return path.
|
||||
func (m *Message) EnableNativeSend() {
|
||||
m.nativeSend = 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) {
|
||||
m.deliveryTime = dt
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SetTrackingClicks information is found in the Mailgun documentation.
|
||||
func (m *Message) SetTrackingClicks(trackingClicks bool) {
|
||||
m.trackingClicks = trackingClicks
|
||||
m.trackingClicksSet = true
|
||||
}
|
||||
|
||||
// SetRequireTLS information is found in the Mailgun documentation.
|
||||
func (m *Message) SetRequireTLS(b bool) {
|
||||
m.requireTLS = b
|
||||
}
|
||||
|
||||
// SetSkipVerification information is found in the Mailgun documentation.
|
||||
func (m *Message) SetSkipVerification(b bool) {
|
||||
m.skipVerification = b
|
||||
}
|
||||
|
||||
//SetTrackingOpens information is found in the Mailgun documentation.
|
||||
func (m *Message) SetTrackingOpens(trackingOpens bool) {
|
||||
m.trackingOpens = trackingOpens
|
||||
m.trackingOpensSet = true
|
||||
}
|
||||
|
||||
//SetTemplateVersion information is found in the Mailgun documentation.
|
||||
func (m *Message) SetTemplateVersion(tag string) {
|
||||
m.templateVersionTag = tag
|
||||
}
|
||||
|
||||
//SetTemplateRenderText information is found in the Mailgun documentation.
|
||||
func (m *Message) SetTemplateRenderText(render bool) {
|
||||
m.templateRenderText = render
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if m.variables == nil {
|
||||
m.variables = make(map[string]string)
|
||||
}
|
||||
|
||||
j, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := string(j)
|
||||
v, err := strconv.Unquote(encoded)
|
||||
if err != nil {
|
||||
v = encoded
|
||||
}
|
||||
|
||||
m.variables[variable] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTemplateVariable adds a template variable to the map of template variables, replacing the variable if it is already there.
|
||||
// This is used for server-side message templates and can nest arbitrary values. At send time, the resulting map will be converted into
|
||||
// a JSON string and sent as a header in the X-Mailgun-Variables header.
|
||||
func (m *Message) AddTemplateVariable(variable string, value interface{}) error {
|
||||
if m.templateVariables == nil {
|
||||
m.templateVariables = make(map[string]interface{})
|
||||
}
|
||||
m.templateVariables[variable] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDomain allows you to use a separate domain for the type of messages you are sending.
|
||||
func (m *Message) AddDomain(domain string) {
|
||||
m.domain = domain
|
||||
}
|
||||
|
||||
// GetHeaders retrieves the http headers associated with this message
|
||||
func (m *Message) GetHeaders() map[string]string {
|
||||
return m.headers
|
||||
}
|
||||
|
||||
// ErrInvalidMessage is 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.
|
||||
// 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 (mg *MailgunImpl) Send(ctx context.Context, message *Message) (mes string, id string, err error) {
|
||||
if mg.domain == "" {
|
||||
err = errors.New("you must provide a valid domain before calling Send()")
|
||||
return
|
||||
}
|
||||
|
||||
if mg.apiKey == "" {
|
||||
err = errors.New("you must provide a valid api-key before calling Send()")
|
||||
return
|
||||
}
|
||||
|
||||
if !isValid(message) {
|
||||
err = ErrInvalidMessage
|
||||
return
|
||||
}
|
||||
payload := 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.IsZero() {
|
||||
payload.addValue("o:deliverytime", formatMailgunTime(message.deliveryTime))
|
||||
}
|
||||
if message.nativeSend {
|
||||
payload.addValue("o:native-send", "yes")
|
||||
}
|
||||
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.requireTLS {
|
||||
payload.addValue("o:require-tls", trueFalse(message.requireTLS))
|
||||
}
|
||||
if message.skipVerification {
|
||||
payload.addValue("o:skip-verification", trueFalse(message.skipVerification))
|
||||
}
|
||||
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.templateVariables != nil {
|
||||
variableString, err := json.Marshal(message.templateVariables)
|
||||
if err == nil {
|
||||
// the map was marshalled as json so add it
|
||||
payload.addValue("h:X-Mailgun-Variables", string(variableString))
|
||||
}
|
||||
}
|
||||
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.bufferAttachments != nil {
|
||||
for _, bufferAttachment := range message.bufferAttachments {
|
||||
payload.addBuffer("attachment", bufferAttachment.Filename, bufferAttachment.Buffer)
|
||||
}
|
||||
}
|
||||
if message.inlines != nil {
|
||||
for _, inline := range message.inlines {
|
||||
payload.addFile("inline", inline)
|
||||
}
|
||||
}
|
||||
|
||||
if message.readerInlines != nil {
|
||||
for _, readerAttachment := range message.readerInlines {
|
||||
payload.addReadCloser("inline", readerAttachment.Filename, readerAttachment.ReadCloser)
|
||||
}
|
||||
}
|
||||
|
||||
if message.domain == "" {
|
||||
message.domain = mg.Domain()
|
||||
}
|
||||
|
||||
if message.templateVersionTag != "" {
|
||||
payload.addValue("t:version", message.templateVersionTag)
|
||||
}
|
||||
|
||||
if message.templateRenderText {
|
||||
payload.addValue("t:text", yesNo(message.templateRenderText))
|
||||
}
|
||||
|
||||
r := newHTTPRequest(generateApiUrlWithDomain(mg, message.specific.endpoint(), message.domain))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var response sendMessageResponse
|
||||
err = postResponseFromJSON(ctx, r, payload, &response)
|
||||
if err == nil {
|
||||
mes = response.Message
|
||||
id = response.Id
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (pm *plainMessage) addValues(p *formDataPayload) {
|
||||
p.addValue("from", pm.from)
|
||||
p.addValue("subject", pm.subject)
|
||||
p.addValue("text", pm.text)
|
||||
for _, cc := range pm.cc {
|
||||
p.addValue("cc", cc)
|
||||
}
|
||||
for _, bcc := range pm.bcc {
|
||||
p.addValue("bcc", bcc)
|
||||
}
|
||||
if pm.html != "" {
|
||||
p.addValue("html", pm.html)
|
||||
}
|
||||
if pm.template != "" {
|
||||
p.addValue("template", pm.template)
|
||||
}
|
||||
if pm.ampHtml != "" {
|
||||
p.addValue("amp-html", pm.ampHtml)
|
||||
}
|
||||
}
|
||||
|
||||
func (mm *mimeMessage) addValues(p *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"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
|
||||
func trueFalse(b bool) string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
// 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 m.RecipientCount() == 0 {
|
||||
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.template != "" {
|
||||
// pm.text or pm.html not needed if template is supplied
|
||||
return true
|
||||
}
|
||||
|
||||
if pm.text == "" && pm.html == "" {
|
||||
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(ctx context.Context, url string) (StoredMessage, error) {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var response StoredMessage
|
||||
err := getResponseFromJSON(ctx, r, &response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Given a storage id resend the stored message to the specified recipients
|
||||
func (mg *MailgunImpl) ReSend(ctx context.Context, url string, recipients ...string) (string, string, error) {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newFormDataPayload()
|
||||
|
||||
if len(recipients) == 0 {
|
||||
return "", "", errors.New("must provide at least one recipient")
|
||||
}
|
||||
|
||||
for _, to := range recipients {
|
||||
payload.addValue("to", to)
|
||||
}
|
||||
|
||||
var resp sendMessageResponse
|
||||
err := postResponseFromJSON(ctx, r, payload, &resp)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return resp.Message, resp.Id, nil
|
||||
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, url string) (StoredMessageRaw, error) {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
r.addHeader("Accept", "message/rfc2822")
|
||||
|
||||
var response StoredMessageRaw
|
||||
err := getResponseFromJSON(ctx, r, &response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Deprecated: Use GetStoreMessage() instead
|
||||
func (mg *MailgunImpl) GetStoredMessageForURL(ctx context.Context, url string) (StoredMessage, error) {
|
||||
return mg.GetStoredMessage(ctx, url)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetStoreMessageRaw() instead
|
||||
func (mg *MailgunImpl) GetStoredMessageRawForURL(ctx context.Context, url string) (StoredMessageRaw, error) {
|
||||
return mg.GetStoredMessageRaw(ctx, url)
|
||||
}
|
||||
|
||||
// GetStoredAttachment retrieves the raw MIME body of a received e-mail message attachment.
|
||||
func (mg *MailgunImpl) GetStoredAttachment(ctx context.Context, url string) ([]byte, error) {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
r.addHeader("Accept", "message/rfc2822")
|
||||
|
||||
response, err := makeGetRequest(ctx, r)
|
||||
|
||||
return response.Data, err
|
||||
}
|
||||
198
vendor/github.com/mailgun/mailgun-go/v4/mock.go
generated
vendored
Normal file
198
vendor/github.com/mailgun/mailgun-go/v4/mock.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// A mailgun api mock suitable for testing
|
||||
type MockServer struct {
|
||||
srv *httptest.Server
|
||||
|
||||
domainIPS []string
|
||||
domainList []domainContainer
|
||||
exportList []Export
|
||||
mailingList []mailingListContainer
|
||||
routeList []Route
|
||||
events []Event
|
||||
webhooks WebHooksListResponse
|
||||
}
|
||||
|
||||
// Create a new instance of the mailgun API mock server
|
||||
func NewMockServer() MockServer {
|
||||
ms := MockServer{}
|
||||
|
||||
// Add all our handlers
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Route("/v3", func(r chi.Router) {
|
||||
ms.addIPRoutes(r)
|
||||
ms.addExportRoutes(r)
|
||||
ms.addDomainRoutes(r)
|
||||
ms.addMailingListRoutes(r)
|
||||
ms.addEventRoutes(r)
|
||||
ms.addMessagesRoutes(r)
|
||||
ms.addRoutes(r)
|
||||
ms.addWebhookRoutes(r)
|
||||
})
|
||||
ms.addValidationRoutes(r)
|
||||
|
||||
// Start the server
|
||||
ms.srv = httptest.NewServer(r)
|
||||
return ms
|
||||
}
|
||||
|
||||
// Stop the server
|
||||
func (ms *MockServer) Stop() {
|
||||
ms.srv.Close()
|
||||
}
|
||||
|
||||
func (ms *MockServer) URL4() string {
|
||||
return ms.srv.URL + "/v4"
|
||||
}
|
||||
|
||||
// URL returns the URL used to connect to the mock server
|
||||
func (ms *MockServer) URL() string {
|
||||
return ms.srv.URL + "/v3"
|
||||
}
|
||||
|
||||
func toJSON(w http.ResponseWriter, obj interface{}) {
|
||||
if err := json.NewEncoder(w).Encode(obj); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
func stringToBool(v string) bool {
|
||||
lower := strings.ToLower(v)
|
||||
if lower == "yes" || lower == "no" {
|
||||
return lower == "yes"
|
||||
}
|
||||
|
||||
if v == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
result, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func stringToInt(v string) int {
|
||||
if v == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
result, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int(result)
|
||||
}
|
||||
|
||||
func stringToMap(v string) map[string]interface{} {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
err := json.Unmarshal([]byte(v), &result)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func parseAddress(v string) string {
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
e, err := mail.ParseAddress(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e.Address
|
||||
}
|
||||
|
||||
// Given the page direction, pivot value and limit, calculate the offsets for the slice
|
||||
func pageOffsets(pivotIdx []string, pivotDir, pivotVal string, limit int) (int, int) {
|
||||
switch pivotDir {
|
||||
case "first":
|
||||
if limit < len(pivotIdx) {
|
||||
return 0, limit
|
||||
}
|
||||
return 0, len(pivotIdx)
|
||||
case "last":
|
||||
if limit < len(pivotIdx) {
|
||||
return len(pivotIdx) - limit, len(pivotIdx)
|
||||
}
|
||||
return 0, len(pivotIdx)
|
||||
case "next":
|
||||
for i, item := range pivotIdx {
|
||||
if item == pivotVal {
|
||||
offset := i + 1 + limit
|
||||
if offset > len(pivotIdx) {
|
||||
offset = len(pivotIdx)
|
||||
}
|
||||
return i + 1, offset
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
case "prev":
|
||||
for i, item := range pivotIdx {
|
||||
if item == pivotVal {
|
||||
if i == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
offset := i - limit
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
return offset, i
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
if limit > len(pivotIdx) {
|
||||
return 0, len(pivotIdx)
|
||||
}
|
||||
return 0, limit
|
||||
}
|
||||
|
||||
func getPageURL(r *http.Request, params url.Values) string {
|
||||
if r.FormValue("limit") != "" {
|
||||
params.Add("limit", r.FormValue("limit"))
|
||||
}
|
||||
return "http://" + r.Host + r.URL.EscapedPath() + "?" + params.Encode()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func randomEmail(prefix, domain string) string {
|
||||
return strings.ToLower(fmt.Sprintf("%s@%s", randomString(20, prefix), domain))
|
||||
}
|
||||
283
vendor/github.com/mailgun/mailgun-go/v4/mock_domains.go
generated
vendored
Normal file
283
vendor/github.com/mailgun/mailgun-go/v4/mock_domains.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
type domainContainer struct {
|
||||
Domain Domain `json:"domain"`
|
||||
ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"`
|
||||
SendingDNSRecords []DNSRecord `json:"sending_dns_records"`
|
||||
Connection *DomainConnection `json:"connection,omitempty"`
|
||||
Tracking *DomainTracking `json:"tracking,omitempty"`
|
||||
TagLimits *TagLimits `json:"limits,omitempty"`
|
||||
}
|
||||
|
||||
func (ms *MockServer) addDomainRoutes(r chi.Router) {
|
||||
|
||||
ms.domainList = append(ms.domainList, domainContainer{
|
||||
Domain: Domain{
|
||||
CreatedAt: RFC2822Time(time.Now().UTC()),
|
||||
Name: "mailgun.test",
|
||||
SMTPLogin: "postmaster@mailgun.test",
|
||||
SMTPPassword: "4rtqo4p6rrx9",
|
||||
Wildcard: true,
|
||||
SpamAction: SpamActionDisabled,
|
||||
State: "active",
|
||||
},
|
||||
Connection: &DomainConnection{
|
||||
RequireTLS: true,
|
||||
SkipVerification: true,
|
||||
},
|
||||
TagLimits: &TagLimits{
|
||||
Limit: 50000,
|
||||
Count: 5000,
|
||||
},
|
||||
Tracking: &DomainTracking{
|
||||
Click: TrackingStatus{Active: true},
|
||||
Open: TrackingStatus{Active: true},
|
||||
Unsubscribe: TrackingStatus{
|
||||
Active: false,
|
||||
HTMLFooter: "\n<br>\n<p><a href=\"%unsubscribe_url%\">unsubscribe</a></p>\n",
|
||||
TextFooter: "\n\nTo unsubscribe click: <%unsubscribe_url%>\n\n",
|
||||
},
|
||||
},
|
||||
ReceivingDNSRecords: []DNSRecord{
|
||||
{
|
||||
Priority: "10",
|
||||
RecordType: "MX",
|
||||
Valid: "valid",
|
||||
Value: "mxa.mailgun.org",
|
||||
},
|
||||
{
|
||||
Priority: "10",
|
||||
RecordType: "MX",
|
||||
Valid: "valid",
|
||||
Value: "mxb.mailgun.org",
|
||||
},
|
||||
},
|
||||
SendingDNSRecords: []DNSRecord{
|
||||
{
|
||||
RecordType: "TXT",
|
||||
Valid: "valid",
|
||||
Name: "domain.com",
|
||||
Value: "v=spf1 include:mailgun.org ~all",
|
||||
},
|
||||
{
|
||||
RecordType: "TXT",
|
||||
Valid: "valid",
|
||||
Name: "domain.com",
|
||||
Value: "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUA....",
|
||||
},
|
||||
{
|
||||
RecordType: "CNAME",
|
||||
Valid: "valid",
|
||||
Name: "email.domain.com",
|
||||
Value: "mailgun.org",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
r.Get("/domains", ms.listDomains)
|
||||
r.Post("/domains", ms.createDomain)
|
||||
r.Get("/domains/{domain}", ms.getDomain)
|
||||
r.Put("/domains/{domain}/verify", ms.getDomain)
|
||||
r.Delete("/domains/{domain}", ms.deleteDomain)
|
||||
//r.Get("/domains/{domain}/credentials", ms.getCredentials)
|
||||
//r.Post("/domains/{domain}/credentials", ms.createCredentials)
|
||||
//r.Put("/domains/{domain}/credentials/{login}", ms.updateCredentials)
|
||||
//r.Delete("/domains/{domain}/credentials/{login}", ms.deleteCredentials)
|
||||
r.Get("/domains/{domain}/connection", ms.getConnection)
|
||||
r.Put("/domains/{domain}/connection", ms.updateConnection)
|
||||
r.Get("/domains/{domain}/tracking", ms.getTracking)
|
||||
r.Put("/domains/{domain}/tracking/click", ms.updateClickTracking)
|
||||
r.Put("/domains/{domain}/tracking/open", ms.updateOpenTracking)
|
||||
r.Put("/domains/{domain}/tracking/unsubscribe", ms.updateUnsubTracking)
|
||||
r.Get("/domains/{domain}/limits/tag", ms.getTagLimits)
|
||||
}
|
||||
|
||||
func (ms *MockServer) listDomains(w http.ResponseWriter, r *http.Request) {
|
||||
var list []Domain
|
||||
for _, domain := range ms.domainList {
|
||||
list = append(list, domain.Domain)
|
||||
}
|
||||
|
||||
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[skip:end],
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getDomain(w http.ResponseWriter, r *http.Request) {
|
||||
for _, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
d.Connection = nil
|
||||
toJSON(w, d)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) createDomain(w http.ResponseWriter, r *http.Request) {
|
||||
ms.domainList = append(ms.domainList, domainContainer{
|
||||
Domain: Domain{
|
||||
CreatedAt: RFC2822Time(time.Now()),
|
||||
Name: r.FormValue("name"),
|
||||
SMTPLogin: r.FormValue("smtp_login"),
|
||||
SMTPPassword: r.FormValue("smtp_password"),
|
||||
Wildcard: stringToBool(r.FormValue("wildcard")),
|
||||
SpamAction: SpamAction(r.FormValue("spam_action")),
|
||||
State: "active",
|
||||
},
|
||||
})
|
||||
toJSON(w, okResp{Message: "Domain has been created"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteDomain(w http.ResponseWriter, r *http.Request) {
|
||||
result := ms.domainList[:0]
|
||||
for _, domain := range ms.domainList {
|
||||
if domain.Domain.Name == chi.URLParam(r, "domain") {
|
||||
continue
|
||||
}
|
||||
result = append(result, domain)
|
||||
}
|
||||
|
||||
if len(result) != len(ms.domainList) {
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
ms.domainList = result
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getConnection(w http.ResponseWriter, r *http.Request) {
|
||||
for _, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
resp := domainConnectionResponse{
|
||||
Connection: *d.Connection,
|
||||
}
|
||||
toJSON(w, resp)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateConnection(w http.ResponseWriter, r *http.Request) {
|
||||
for i, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
ms.domainList[i].Connection = &DomainConnection{
|
||||
RequireTLS: stringToBool(r.FormValue("require_tls")),
|
||||
SkipVerification: stringToBool(r.FormValue("skip_verification")),
|
||||
}
|
||||
toJSON(w, okResp{Message: "Domain connection settings have been updated, may take 10 minutes to fully propagate"})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getTracking(w http.ResponseWriter, r *http.Request) {
|
||||
for _, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
resp := domainTrackingResponse{
|
||||
Tracking: *d.Tracking,
|
||||
}
|
||||
toJSON(w, resp)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateClickTracking(w http.ResponseWriter, r *http.Request) {
|
||||
for i, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
ms.domainList[i].Tracking.Click.Active = stringToBool(r.FormValue("active"))
|
||||
toJSON(w, okResp{Message: "Domain tracking settings have been updated"})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateOpenTracking(w http.ResponseWriter, r *http.Request) {
|
||||
for i, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
ms.domainList[i].Tracking.Open.Active = stringToBool(r.FormValue("active"))
|
||||
toJSON(w, okResp{Message: "Domain tracking settings have been updated"})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateUnsubTracking(w http.ResponseWriter, r *http.Request) {
|
||||
for i, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
ms.domainList[i].Tracking.Unsubscribe.Active = stringToBool(r.FormValue("active"))
|
||||
if len(r.FormValue("html_footer")) != 0 {
|
||||
ms.domainList[i].Tracking.Unsubscribe.HTMLFooter = r.FormValue("html_footer")
|
||||
}
|
||||
if len(r.FormValue("text_footer")) != 0 {
|
||||
ms.domainList[i].Tracking.Unsubscribe.TextFooter = r.FormValue("text_footer")
|
||||
}
|
||||
toJSON(w, okResp{Message: "Domain tracking settings have been updated"})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getTagLimits(w http.ResponseWriter, r *http.Request) {
|
||||
for _, d := range ms.domainList {
|
||||
if d.Domain.Name == chi.URLParam(r, "domain") {
|
||||
if d.TagLimits == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "no limits defined for domain"})
|
||||
return
|
||||
}
|
||||
toJSON(w, d.TagLimits)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "domain not found"})
|
||||
}
|
||||
242
vendor/github.com/mailgun/mailgun-go/v4/mock_events.go
generated
vendored
Normal file
242
vendor/github.com/mailgun/mailgun-go/v4/mock_events.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addEventRoutes(r chi.Router) {
|
||||
r.Get("/{domain}/events", ms.listEvents)
|
||||
|
||||
var (
|
||||
tags = []string{"tag1", "tag2"}
|
||||
recipients = []string{"one@mailgun.test", "two@mailgun.test"}
|
||||
recipientDomain = "mailgun.test"
|
||||
timeStamp = TimeToFloat(time.Now().UTC())
|
||||
ipAddress = "192.168.1.1"
|
||||
message = events.Message{Headers: events.MessageHeaders{MessageID: "1234"}}
|
||||
clientInfo = events.ClientInfo{
|
||||
AcceptLanguage: "EN",
|
||||
ClientName: "Firefox",
|
||||
ClientOS: "OS X",
|
||||
ClientType: "browser",
|
||||
DeviceType: "desktop",
|
||||
IP: "8.8.8.8",
|
||||
UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0",
|
||||
}
|
||||
geoLocation = events.GeoLocation{
|
||||
City: "San Antonio",
|
||||
Country: "US",
|
||||
Region: "TX",
|
||||
}
|
||||
)
|
||||
|
||||
// AcceptedNoAuth
|
||||
accepted := new(events.Accepted)
|
||||
accepted.ID = randomString(16, "ID-")
|
||||
accepted.Message.Headers.MessageID = accepted.ID
|
||||
accepted.Name = events.EventAccepted
|
||||
accepted.Tags = tags
|
||||
accepted.Timestamp = timeStamp
|
||||
accepted.Recipient = recipients[0]
|
||||
accepted.RecipientDomain = recipientDomain
|
||||
accepted.Flags = events.Flags{
|
||||
IsAuthenticated: false,
|
||||
}
|
||||
ms.events = append(ms.events, accepted)
|
||||
|
||||
// AcceptedAuth
|
||||
accepted = new(events.Accepted)
|
||||
accepted.ID = randomString(16, "ID-")
|
||||
accepted.Message.Headers.MessageID = accepted.ID
|
||||
accepted.Name = events.EventAccepted
|
||||
accepted.Tags = tags
|
||||
accepted.Timestamp = timeStamp
|
||||
accepted.Recipient = recipients[0]
|
||||
accepted.RecipientDomain = recipientDomain
|
||||
accepted.Campaigns = []events.Campaign{
|
||||
{ID: "test-id", Name: "test"},
|
||||
}
|
||||
accepted.Flags = events.Flags{
|
||||
IsAuthenticated: true,
|
||||
}
|
||||
ms.events = append(ms.events, accepted)
|
||||
|
||||
// DeliveredSMTP
|
||||
delivered := new(events.Delivered)
|
||||
delivered.ID = randomString(16, "ID-")
|
||||
delivered.Message.Headers.MessageID = delivered.ID
|
||||
delivered.Name = events.EventDelivered
|
||||
delivered.Tags = tags
|
||||
delivered.Timestamp = timeStamp
|
||||
delivered.Recipient = recipients[0]
|
||||
delivered.RecipientDomain = recipientDomain
|
||||
delivered.DeliveryStatus.Message = "We sent an email Yo"
|
||||
delivered.Envelope = events.Envelope{
|
||||
Transport: "smtp",
|
||||
SendingIP: ipAddress,
|
||||
}
|
||||
delivered.Flags = events.Flags{
|
||||
IsAuthenticated: true,
|
||||
}
|
||||
ms.events = append(ms.events, delivered)
|
||||
|
||||
// DeliveredHTTP
|
||||
delivered = new(events.Delivered)
|
||||
delivered.ID = randomString(16, "ID-")
|
||||
delivered.Message.Headers.MessageID = delivered.ID
|
||||
delivered.Name = events.EventDelivered
|
||||
delivered.Tags = tags
|
||||
delivered.Timestamp = timeStamp
|
||||
delivered.Recipient = recipients[0]
|
||||
delivered.RecipientDomain = recipientDomain
|
||||
delivered.DeliveryStatus.Message = "We sent an email Yo"
|
||||
delivered.Envelope = events.Envelope{
|
||||
Transport: "http",
|
||||
SendingIP: ipAddress,
|
||||
}
|
||||
delivered.Flags = events.Flags{
|
||||
IsAuthenticated: true,
|
||||
}
|
||||
ms.events = append(ms.events, delivered)
|
||||
|
||||
// Stored
|
||||
stored := new(events.Stored)
|
||||
stored.ID = randomString(16, "ID-")
|
||||
stored.Name = events.EventStored
|
||||
stored.Tags = tags
|
||||
stored.Timestamp = timeStamp
|
||||
stored.Storage.URL = "http://mailgun.text/some/url"
|
||||
ms.events = append(ms.events, stored)
|
||||
|
||||
// Clicked
|
||||
for _, recipient := range recipients {
|
||||
clicked := new(events.Clicked)
|
||||
clicked.ID = randomString(16, "ID-")
|
||||
clicked.Name = events.EventClicked
|
||||
clicked.Message = message
|
||||
clicked.Tags = tags
|
||||
clicked.Recipient = recipient
|
||||
clicked.ClientInfo = clientInfo
|
||||
clicked.GeoLocation = geoLocation
|
||||
clicked.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, clicked)
|
||||
}
|
||||
|
||||
clicked := new(events.Clicked)
|
||||
clicked.ID = randomString(16, "ID-")
|
||||
clicked.Name = events.EventClicked
|
||||
clicked.Message = message
|
||||
clicked.Tags = tags
|
||||
clicked.Recipient = recipients[0]
|
||||
clicked.ClientInfo = clientInfo
|
||||
clicked.GeoLocation = geoLocation
|
||||
clicked.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, clicked)
|
||||
|
||||
// Opened
|
||||
for _, recipient := range recipients {
|
||||
opened := new(events.Opened)
|
||||
opened.ID = randomString(16, "ID-")
|
||||
opened.Name = events.EventOpened
|
||||
opened.Message = message
|
||||
opened.Tags = tags
|
||||
opened.Recipient = recipient
|
||||
opened.ClientInfo = clientInfo
|
||||
opened.GeoLocation = geoLocation
|
||||
opened.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, opened)
|
||||
}
|
||||
|
||||
opened := new(events.Opened)
|
||||
opened.ID = randomString(16, "ID-")
|
||||
opened.Name = events.EventOpened
|
||||
opened.Message = message
|
||||
opened.Tags = tags
|
||||
opened.Recipient = recipients[0]
|
||||
opened.ClientInfo = clientInfo
|
||||
opened.GeoLocation = geoLocation
|
||||
opened.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, opened)
|
||||
|
||||
// Unsubscribed
|
||||
for _, recipient := range recipients {
|
||||
unsub := new(events.Unsubscribed)
|
||||
unsub.ID = randomString(16, "ID-")
|
||||
unsub.Name = events.EventUnsubscribed
|
||||
unsub.Tags = tags
|
||||
unsub.Recipient = recipient
|
||||
unsub.ClientInfo = clientInfo
|
||||
unsub.GeoLocation = geoLocation
|
||||
unsub.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, unsub)
|
||||
}
|
||||
|
||||
// Complained
|
||||
for _, recipient := range recipients {
|
||||
complained := new(events.Complained)
|
||||
complained.ID = randomString(16, "ID-")
|
||||
complained.Name = events.EventComplained
|
||||
complained.Tags = tags
|
||||
complained.Recipient = recipient
|
||||
complained.Timestamp = timeStamp
|
||||
ms.events = append(ms.events, complained)
|
||||
}
|
||||
}
|
||||
|
||||
type eventsResponse struct {
|
||||
Items []Event `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
func (ms *MockServer) listEvents(w http.ResponseWriter, r *http.Request) {
|
||||
var idx []string
|
||||
|
||||
for _, e := range ms.events {
|
||||
idx = append(idx, e.GetID())
|
||||
}
|
||||
|
||||
limit := stringToInt(r.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
start, end := pageOffsets(idx, r.FormValue("page"), r.FormValue("address"), limit)
|
||||
|
||||
var nextAddress, prevAddress string
|
||||
var results []Event
|
||||
|
||||
if start != end {
|
||||
results = ms.events[start:end]
|
||||
nextAddress = results[len(results)-1].GetID()
|
||||
prevAddress = results[0].GetID()
|
||||
} else {
|
||||
results = []Event{}
|
||||
nextAddress = r.FormValue("address")
|
||||
prevAddress = r.FormValue("address")
|
||||
}
|
||||
|
||||
resp := eventsResponse{
|
||||
Paging: Paging{
|
||||
First: getPageURL(r, url.Values{
|
||||
"page": []string{"first"},
|
||||
}),
|
||||
Last: getPageURL(r, url.Values{
|
||||
"page": []string{"last"},
|
||||
}),
|
||||
Next: getPageURL(r, url.Values{
|
||||
"page": []string{"next"},
|
||||
"address": []string{nextAddress},
|
||||
}),
|
||||
Previous: getPageURL(r, url.Values{
|
||||
"page": []string{"prev"},
|
||||
"address": []string{prevAddress},
|
||||
}),
|
||||
},
|
||||
Items: results,
|
||||
}
|
||||
toJSON(w, resp)
|
||||
}
|
||||
48
vendor/github.com/mailgun/mailgun-go/v4/mock_exports.go
generated
vendored
Normal file
48
vendor/github.com/mailgun/mailgun-go/v4/mock_exports.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addExportRoutes(r chi.Router) {
|
||||
r.Post("/exports", ms.postExports)
|
||||
r.Get("/exports", ms.listExports)
|
||||
r.Get("/exports/{id}", ms.getExport)
|
||||
r.Get("/exports/{id}/download_url", ms.getExportLink)
|
||||
}
|
||||
|
||||
func (ms *MockServer) postExports(w http.ResponseWriter, r *http.Request) {
|
||||
e := Export{
|
||||
ID: strconv.Itoa(len(ms.exportList)),
|
||||
URL: r.FormValue("url"),
|
||||
Status: "complete",
|
||||
}
|
||||
|
||||
ms.exportList = append(ms.exportList, e)
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) listExports(w http.ResponseWriter, _ *http.Request) {
|
||||
toJSON(w, ExportList{
|
||||
Items: ms.exportList,
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getExport(w http.ResponseWriter, r *http.Request) {
|
||||
for _, export := range ms.exportList {
|
||||
if export.ID == chi.URLParam(r, "id") {
|
||||
toJSON(w, export)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "export not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getExportLink(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", "/some/s3/url")
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}
|
||||
65
vendor/github.com/mailgun/mailgun-go/v4/mock_ips.go
generated
vendored
Normal file
65
vendor/github.com/mailgun/mailgun-go/v4/mock_ips.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addIPRoutes(r chi.Router) {
|
||||
r.Get("/ips", ms.listIPS)
|
||||
r.Get("/ips/{ip}", ms.getIPAddress)
|
||||
r.Route("/domains/{domain}/ips", func(r chi.Router) {
|
||||
r.Get("/", ms.listDomainIPS)
|
||||
r.Get("/{ip}", ms.getIPAddress)
|
||||
r.Post("/", ms.postDomainIPS)
|
||||
r.Delete("/{ip}", ms.deleteDomainIPS)
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) listIPS(w http.ResponseWriter, _ *http.Request) {
|
||||
toJSON(w, ipAddressListResponse{
|
||||
TotalCount: 2,
|
||||
Items: []string{"172.0.0.1", "192.168.1.1"},
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||
toJSON(w, IPAddress{
|
||||
IP: chi.URLParam(r, "ip"),
|
||||
RDNS: "luna.mailgun.net",
|
||||
Dedicated: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) listDomainIPS(w http.ResponseWriter, _ *http.Request) {
|
||||
toJSON(w, ipAddressListResponse{
|
||||
TotalCount: 2,
|
||||
Items: ms.domainIPS,
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) postDomainIPS(w http.ResponseWriter, r *http.Request) {
|
||||
ms.domainIPS = append(ms.domainIPS, r.FormValue("ip"))
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteDomainIPS(w http.ResponseWriter, r *http.Request) {
|
||||
result := ms.domainIPS[:0]
|
||||
for _, ip := range ms.domainIPS {
|
||||
if ip == chi.URLParam(r, "ip") {
|
||||
continue
|
||||
}
|
||||
result = append(result, ip)
|
||||
}
|
||||
|
||||
if len(result) != len(ms.domainIPS) {
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
ms.domainIPS = result
|
||||
return
|
||||
}
|
||||
|
||||
// Not the actual error returned by the mailgun API
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "ip not found"})
|
||||
}
|
||||
389
vendor/github.com/mailgun/mailgun-go/v4/mock_mailing_list.go
generated
vendored
Normal file
389
vendor/github.com/mailgun/mailgun-go/v4/mock_mailing_list.go
generated
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
type mailingListContainer struct {
|
||||
MailingList MailingList
|
||||
Members []Member
|
||||
}
|
||||
|
||||
func (ms *MockServer) addMailingListRoutes(r chi.Router) {
|
||||
r.Get("/lists/pages", ms.listMailingLists)
|
||||
r.Get("/lists/{address}", ms.getMailingList)
|
||||
r.Post("/lists", ms.createMailingList)
|
||||
r.Put("/lists/{address}", ms.updateMailingList)
|
||||
r.Delete("/lists/{address}", ms.deleteMailingList)
|
||||
|
||||
r.Get("/lists/{address}/members/pages", ms.listMembers)
|
||||
r.Get("/lists/{address}/members/{member}", ms.getMember)
|
||||
r.Post("/lists/{address}/members", ms.createMember)
|
||||
r.Put("/lists/{address}/members/{member}", ms.updateMember)
|
||||
r.Delete("/lists/{address}/members/{member}", ms.deleteMember)
|
||||
r.Post("/lists/{address}/members.json", ms.bulkCreate)
|
||||
|
||||
ms.mailingList = append(ms.mailingList, mailingListContainer{
|
||||
MailingList: MailingList{
|
||||
AccessLevel: "everyone",
|
||||
Address: "foo@mailgun.test",
|
||||
CreatedAt: RFC2822Time(time.Now().UTC()),
|
||||
Description: "Mailgun developers list",
|
||||
MembersCount: 1,
|
||||
Name: "",
|
||||
},
|
||||
Members: []Member{
|
||||
{
|
||||
Address: "dev@samples.mailgun.org",
|
||||
Name: "Developer",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) listMailingLists(w http.ResponseWriter, r *http.Request) {
|
||||
var list []MailingList
|
||||
var idx []string
|
||||
|
||||
for _, ml := range ms.mailingList {
|
||||
list = append(list, ml.MailingList)
|
||||
idx = append(idx, ml.MailingList.Address)
|
||||
}
|
||||
|
||||
limit := stringToInt(r.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
start, end := pageOffsets(idx, r.FormValue("page"), r.FormValue("address"), limit)
|
||||
results := list[start:end]
|
||||
|
||||
if len(results) == 0 {
|
||||
toJSON(w, listsResponse{})
|
||||
return
|
||||
}
|
||||
|
||||
resp := listsResponse{
|
||||
Paging: Paging{
|
||||
First: getPageURL(r, url.Values{
|
||||
"page": []string{"first"},
|
||||
}),
|
||||
Last: getPageURL(r, url.Values{
|
||||
"page": []string{"last"},
|
||||
}),
|
||||
Next: getPageURL(r, url.Values{
|
||||
"page": []string{"next"},
|
||||
"address": []string{results[len(results)-1].Address},
|
||||
}),
|
||||
Previous: getPageURL(r, url.Values{
|
||||
"page": []string{"prev"},
|
||||
"address": []string{results[0].Address},
|
||||
}),
|
||||
},
|
||||
Items: results,
|
||||
}
|
||||
toJSON(w, resp)
|
||||
}
|
||||
|
||||
func (ms *MockServer) getMailingList(w http.ResponseWriter, r *http.Request) {
|
||||
for _, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
toJSON(w, mailingListResponse{MailingList: ml.MailingList})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteMailingList(w http.ResponseWriter, r *http.Request) {
|
||||
result := ms.mailingList[:0]
|
||||
for _, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
continue
|
||||
}
|
||||
result = append(result, ml)
|
||||
}
|
||||
|
||||
if len(result) != len(ms.mailingList) {
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
ms.mailingList = result
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateMailingList(w http.ResponseWriter, r *http.Request) {
|
||||
for i, d := range ms.mailingList {
|
||||
if d.MailingList.Address == chi.URLParam(r, "address") {
|
||||
if r.FormValue("address") != "" {
|
||||
ms.mailingList[i].MailingList.Address = r.FormValue("address")
|
||||
}
|
||||
if r.FormValue("name") != "" {
|
||||
ms.mailingList[i].MailingList.Name = r.FormValue("name")
|
||||
}
|
||||
if r.FormValue("description") != "" {
|
||||
ms.mailingList[i].MailingList.Description = r.FormValue("description")
|
||||
}
|
||||
if 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
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) createMailingList(w http.ResponseWriter, r *http.Request) {
|
||||
ms.mailingList = append(ms.mailingList, mailingListContainer{
|
||||
MailingList: MailingList{
|
||||
CreatedAt: RFC2822Time(time.Now().UTC()),
|
||||
Name: r.FormValue("name"),
|
||||
Address: r.FormValue("address"),
|
||||
Description: r.FormValue("description"),
|
||||
AccessLevel: AccessLevel(r.FormValue("access_level")),
|
||||
},
|
||||
})
|
||||
toJSON(w, okResp{Message: "Mailing list has been created"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) listMembers(w http.ResponseWriter, r *http.Request) {
|
||||
var list []Member
|
||||
var idx []string
|
||||
var found bool
|
||||
|
||||
for _, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
found = true
|
||||
for _, member := range ml.Members {
|
||||
list = append(list, member)
|
||||
idx = append(idx, member.Address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
limit := stringToInt(r.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
start, end := pageOffsets(idx, r.FormValue("page"), r.FormValue("address"), limit)
|
||||
results := list[start:end]
|
||||
|
||||
if len(results) == 0 {
|
||||
toJSON(w, memberListResponse{})
|
||||
return
|
||||
}
|
||||
|
||||
resp := memberListResponse{
|
||||
Paging: Paging{
|
||||
First: getPageURL(r, url.Values{
|
||||
"page": []string{"first"},
|
||||
}),
|
||||
Last: getPageURL(r, url.Values{
|
||||
"page": []string{"last"},
|
||||
}),
|
||||
Next: getPageURL(r, url.Values{
|
||||
"page": []string{"next"},
|
||||
"address": []string{results[len(results)-1].Address},
|
||||
}),
|
||||
Previous: getPageURL(r, url.Values{
|
||||
"page": []string{"prev"},
|
||||
"address": []string{results[0].Address},
|
||||
}),
|
||||
},
|
||||
Lists: results,
|
||||
}
|
||||
toJSON(w, resp)
|
||||
}
|
||||
|
||||
func (ms *MockServer) getMember(w http.ResponseWriter, r *http.Request) {
|
||||
var found bool
|
||||
for _, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
found = true
|
||||
for _, member := range ml.Members {
|
||||
if member.Address == chi.URLParam(r, "member") {
|
||||
toJSON(w, memberResponse{Member: member})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "member not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteMember(w http.ResponseWriter, r *http.Request) {
|
||||
idx := -1
|
||||
for i, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
result := ms.mailingList[idx].Members[:0]
|
||||
for _, m := range ms.mailingList[idx].Members {
|
||||
if m.Address == chi.URLParam(r, "member") {
|
||||
continue
|
||||
}
|
||||
result = append(result, m)
|
||||
}
|
||||
|
||||
if len(result) != len(ms.mailingList[idx].Members) {
|
||||
toJSON(w, okResp{Message: "Mailing list member has been deleted"})
|
||||
ms.mailingList[idx].Members = result
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "member not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateMember(w http.ResponseWriter, r *http.Request) {
|
||||
idx := -1
|
||||
for i, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
for i, m := range ms.mailingList[idx].Members {
|
||||
if m.Address == chi.URLParam(r, "member") {
|
||||
if r.FormValue("address") != "" {
|
||||
ms.mailingList[idx].Members[i].Address = parseAddress(r.FormValue("address"))
|
||||
}
|
||||
if r.FormValue("name") != "" {
|
||||
ms.mailingList[idx].Members[i].Name = r.FormValue("name")
|
||||
}
|
||||
if r.FormValue("vars") != "" {
|
||||
ms.mailingList[idx].Members[i].Vars = stringToMap(r.FormValue("vars"))
|
||||
}
|
||||
if r.FormValue("subscribed") != "" {
|
||||
sub := stringToBool(r.FormValue("subscribed"))
|
||||
ms.mailingList[idx].Members[i].Subscribed = &sub
|
||||
}
|
||||
toJSON(w, okResp{Message: "Mailing list member has been updated"})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "member not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) createMember(w http.ResponseWriter, r *http.Request) {
|
||||
idx := -1
|
||||
for i, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
sub := stringToBool(r.FormValue("subscribed"))
|
||||
|
||||
if len(ms.mailingList[idx].Members) != 0 {
|
||||
for i, m := range ms.mailingList[idx].Members {
|
||||
if m.Address == r.FormValue("address") {
|
||||
if !stringToBool(r.FormValue("upsert")) {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
toJSON(w, okResp{Message: "member already exists"})
|
||||
return
|
||||
}
|
||||
|
||||
ms.mailingList[idx].Members[i].Address = parseAddress(r.FormValue("address"))
|
||||
ms.mailingList[idx].Members[i].Name = r.FormValue("name")
|
||||
ms.mailingList[idx].Members[i].Vars = stringToMap(r.FormValue("vars"))
|
||||
ms.mailingList[idx].Members[i].Subscribed = &sub
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, Member{
|
||||
Name: r.FormValue("name"),
|
||||
Address: parseAddress(r.FormValue("address")),
|
||||
Vars: stringToMap(r.FormValue("vars")),
|
||||
Subscribed: &sub,
|
||||
})
|
||||
toJSON(w, okResp{Message: "Mailing list member has been created"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) bulkCreate(w http.ResponseWriter, r *http.Request) {
|
||||
idx := -1
|
||||
for i, ml := range ms.mailingList {
|
||||
if ml.MailingList.Address == chi.URLParam(r, "address") {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "mailing list not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var bulkList []Member
|
||||
if err := json.Unmarshal([]byte(r.FormValue("members")), &bulkList); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
toJSON(w, okResp{Message: "while un-marshalling 'members' param - " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
BULK:
|
||||
for _, member := range bulkList {
|
||||
member.Address = parseAddress(member.Address)
|
||||
if len(ms.mailingList[idx].Members) != 0 {
|
||||
for i, m := range ms.mailingList[idx].Members {
|
||||
if m.Address == member.Address {
|
||||
if !stringToBool(r.FormValue("upsert")) {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
toJSON(w, okResp{Message: "member already exists"})
|
||||
return
|
||||
}
|
||||
ms.mailingList[idx].Members[i] = member
|
||||
continue BULK
|
||||
}
|
||||
}
|
||||
}
|
||||
ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, member)
|
||||
}
|
||||
toJSON(w, okResp{Message: "Mailing list has been updated"})
|
||||
}
|
||||
123
vendor/github.com/mailgun/mailgun-go/v4/mock_messages.go
generated
vendored
Normal file
123
vendor/github.com/mailgun/mailgun-go/v4/mock_messages.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addMessagesRoutes(r chi.Router) {
|
||||
r.Post("/{domain}/messages", ms.createMessages)
|
||||
|
||||
// This path is made up; it could be anything as the storage url could change over time
|
||||
r.Get("/se.storage.url/messages/{id}", ms.getStoredMessages)
|
||||
r.Post("/se.storage.url/messages/{id}", ms.sendStoredMessages)
|
||||
}
|
||||
|
||||
// TODO: This implementation doesn't support multiple recipients
|
||||
func (ms *MockServer) createMessages(w http.ResponseWriter, r *http.Request) {
|
||||
to, err := mail.ParseAddress(r.FormValue("to"))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: "invalid 'to' address"})
|
||||
return
|
||||
}
|
||||
id := randomString(16, "ID-")
|
||||
|
||||
switch to.Address {
|
||||
case "stored@mailgun.test":
|
||||
stored := new(events.Stored)
|
||||
stored.Name = events.EventStored
|
||||
stored.Timestamp = TimeToFloat(time.Now().UTC())
|
||||
stored.ID = id
|
||||
stored.Storage.URL = ms.URL() + "/se.storage.url/messages/" + id
|
||||
stored.Storage.Key = id
|
||||
stored.Message.Headers = events.MessageHeaders{
|
||||
Subject: r.FormValue("subject"),
|
||||
From: r.FormValue("from"),
|
||||
To: to.Address,
|
||||
MessageID: id,
|
||||
}
|
||||
stored.Message.Recipients = []string{
|
||||
r.FormValue("to"),
|
||||
}
|
||||
stored.Message.Size = 10
|
||||
stored.Flags = events.Flags{
|
||||
IsTestMode: false,
|
||||
}
|
||||
ms.events = append(ms.events, stored)
|
||||
default:
|
||||
accepted := new(events.Accepted)
|
||||
accepted.Name = events.EventAccepted
|
||||
accepted.ID = id
|
||||
accepted.Timestamp = TimeToFloat(time.Now().UTC())
|
||||
accepted.Message.Headers.From = r.FormValue("from")
|
||||
accepted.Message.Headers.To = r.FormValue("to")
|
||||
accepted.Message.Headers.MessageID = accepted.ID
|
||||
accepted.Message.Headers.Subject = r.FormValue("subject")
|
||||
|
||||
accepted.Recipient = r.FormValue("to")
|
||||
accepted.RecipientDomain = strings.Split(to.Address, "@")[1]
|
||||
accepted.Flags = events.Flags{
|
||||
IsAuthenticated: true,
|
||||
}
|
||||
ms.events = append(ms.events, accepted)
|
||||
}
|
||||
|
||||
toJSON(w, okResp{ID: "<" + id + ">", Message: "Queued. Thank you."})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getStoredMessages(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
// Find our stored event
|
||||
var stored *events.Stored
|
||||
for _, event := range ms.events {
|
||||
if event.GetID() == id {
|
||||
stored = event.(*events.Stored)
|
||||
}
|
||||
}
|
||||
|
||||
if stored == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "not found"})
|
||||
}
|
||||
|
||||
toJSON(w, StoredMessage{
|
||||
Recipients: strings.Join(stored.Message.Recipients, ","),
|
||||
Sender: stored.Message.Headers.From,
|
||||
Subject: stored.Message.Headers.Subject,
|
||||
From: stored.Message.Headers.From,
|
||||
MessageHeaders: [][]string{
|
||||
{"Sender", stored.Message.Headers.From},
|
||||
{"To", stored.Message.Headers.To},
|
||||
{"Subject", stored.Message.Headers.Subject},
|
||||
{"Content-Type", "text/plain"},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) sendStoredMessages(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
// Find our stored event
|
||||
var stored *events.Stored
|
||||
for _, event := range ms.events {
|
||||
if event.GetID() == id {
|
||||
stored = event.(*events.Stored)
|
||||
}
|
||||
}
|
||||
|
||||
if stored == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "not found"})
|
||||
}
|
||||
|
||||
// DO NOTHING
|
||||
|
||||
toJSON(w, okResp{ID: "<" + id + ">", Message: "Queued. Thank you."})
|
||||
}
|
||||
139
vendor/github.com/mailgun/mailgun-go/v4/mock_routes.go
generated
vendored
Normal file
139
vendor/github.com/mailgun/mailgun-go/v4/mock_routes.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-chi/chi"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type routeResponse struct {
|
||||
Route Route `json:"route"`
|
||||
}
|
||||
|
||||
func (ms *MockServer) addRoutes(r chi.Router) {
|
||||
r.Post("/routes", ms.createRoute)
|
||||
r.Get("/routes", ms.listRoutes)
|
||||
r.Get("/routes/{id}", ms.getRoute)
|
||||
r.Put("/routes/{id}", ms.updateRoute)
|
||||
r.Delete("/routes/{id}", ms.deleteRoute)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
ms.routeList = append(ms.routeList, Route{
|
||||
Id: randomString(10, "ID-"),
|
||||
Priority: 0,
|
||||
Description: fmt.Sprintf("Sample Route %d", i),
|
||||
Actions: []string{
|
||||
`forward("http://myhost.com/messages/")`,
|
||||
`stop()`,
|
||||
},
|
||||
Expression: `match_recipient(".*@samples.mailgun.org")`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MockServer) listRoutes(w http.ResponseWriter, r *http.Request) {
|
||||
skip := stringToInt(r.FormValue("skip"))
|
||||
limit := stringToInt(r.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
if skip > len(ms.routeList) {
|
||||
skip = len(ms.routeList)
|
||||
}
|
||||
|
||||
end := limit + skip
|
||||
if end > len(ms.routeList) {
|
||||
end = len(ms.routeList)
|
||||
}
|
||||
|
||||
// If we are at the end of the list
|
||||
if skip == end {
|
||||
toJSON(w, routesListResponse{
|
||||
TotalCount: len(ms.routeList),
|
||||
Items: []Route{},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
toJSON(w, routesListResponse{
|
||||
TotalCount: len(ms.routeList),
|
||||
Items: ms.routeList[skip:end],
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) getRoute(w http.ResponseWriter, r *http.Request) {
|
||||
for _, item := range ms.routeList {
|
||||
if item.Id == chi.URLParam(r, "id") {
|
||||
toJSON(w, routeResponse{Route: item})
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "route not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) createRoute(w http.ResponseWriter, r *http.Request) {
|
||||
if r.FormValue("action") == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: "'action' parameter is required"})
|
||||
return
|
||||
}
|
||||
|
||||
ms.routeList = append(ms.routeList, Route{
|
||||
CreatedAt: RFC2822Time(time.Now().UTC()),
|
||||
Id: randomString(10, "ID-"),
|
||||
Priority: stringToInt(r.FormValue("priority")),
|
||||
Description: r.FormValue("description"),
|
||||
Expression: r.FormValue("expression"),
|
||||
Actions: r.Form["action"],
|
||||
})
|
||||
toJSON(w, createRouteResp{
|
||||
Message: "Route has been created",
|
||||
Route: ms.routeList[len(ms.routeList)-1],
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MockServer) updateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
for i, item := range ms.routeList {
|
||||
if item.Id == chi.URLParam(r, "id") {
|
||||
|
||||
if r.FormValue("action") != "" {
|
||||
ms.routeList[i].Actions = r.Form["action"]
|
||||
}
|
||||
if r.FormValue("priority") != "" {
|
||||
ms.routeList[i].Priority = stringToInt(r.FormValue("priority"))
|
||||
}
|
||||
if r.FormValue("description") != "" {
|
||||
ms.routeList[i].Description = r.FormValue("description")
|
||||
}
|
||||
if r.FormValue("expression") != "" {
|
||||
ms.routeList[i].Expression = r.FormValue("expression")
|
||||
}
|
||||
toJSON(w, ms.routeList[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "route not found"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteRoute(w http.ResponseWriter, r *http.Request) {
|
||||
result := ms.routeList[:0]
|
||||
for _, item := range ms.routeList {
|
||||
if item.Id == chi.URLParam(r, "id") {
|
||||
continue
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
if len(result) != len(ms.domainList) {
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
ms.routeList = result
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "route not found"})
|
||||
}
|
||||
76
vendor/github.com/mailgun/mailgun-go/v4/mock_validation.go
generated
vendored
Normal file
76
vendor/github.com/mailgun/mailgun-go/v4/mock_validation.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addValidationRoutes(r chi.Router) {
|
||||
r.Get("/v3/address/validate", ms.validateEmail)
|
||||
r.Get("/v3/address/parse", ms.parseEmail)
|
||||
r.Get("/v3/address/private/validate", ms.validateEmail)
|
||||
r.Get("/v3/address/private/parse", ms.parseEmail)
|
||||
r.Get("/v4/address/validate", ms.validateEmailV4)
|
||||
}
|
||||
|
||||
func (ms *MockServer) validateEmailV4(w http.ResponseWriter, r *http.Request) {
|
||||
if r.FormValue("address") == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: "'address' parameter is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var results v4EmailValidationResp
|
||||
parts, err := mail.ParseAddress(r.FormValue("address"))
|
||||
if err == nil {
|
||||
results.IsValid = true
|
||||
results.Parts.Domain = strings.Split(parts.Address, "@")[1]
|
||||
results.Parts.LocalPart = strings.Split(parts.Address, "@")[0]
|
||||
results.Parts.DisplayName = parts.Name
|
||||
}
|
||||
results.Reason = []string{"no-reason"}
|
||||
toJSON(w, results)
|
||||
}
|
||||
|
||||
func (ms *MockServer) validateEmail(w http.ResponseWriter, r *http.Request) {
|
||||
if r.FormValue("address") == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: "'address' parameter is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var results EmailVerification
|
||||
parts, err := mail.ParseAddress(r.FormValue("address"))
|
||||
if err == nil {
|
||||
results.IsValid = true
|
||||
results.Parts.Domain = strings.Split(parts.Address, "@")[1]
|
||||
results.Parts.LocalPart = strings.Split(parts.Address, "@")[0]
|
||||
results.Parts.DisplayName = parts.Name
|
||||
}
|
||||
results.Reason = "no-reason"
|
||||
toJSON(w, results)
|
||||
}
|
||||
|
||||
func (ms *MockServer) parseEmail(w http.ResponseWriter, r *http.Request) {
|
||||
if r.FormValue("addresses") == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: "'addresses' parameter is required"})
|
||||
return
|
||||
}
|
||||
|
||||
addresses := strings.Split(r.FormValue("addresses"), ",")
|
||||
|
||||
var results addressParseResult
|
||||
for _, address := range addresses {
|
||||
_, err := mail.ParseAddress(address)
|
||||
if err != nil {
|
||||
results.Unparseable = append(results.Unparseable, address)
|
||||
} else {
|
||||
results.Parsed = append(results.Parsed, address)
|
||||
}
|
||||
}
|
||||
toJSON(w, results)
|
||||
}
|
||||
83
vendor/github.com/mailgun/mailgun-go/v4/mock_webhooks.go
generated
vendored
Normal file
83
vendor/github.com/mailgun/mailgun-go/v4/mock_webhooks.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (ms *MockServer) addWebhookRoutes(r chi.Router) {
|
||||
r.Route("/domains/{domain}/webhooks", func(r chi.Router) {
|
||||
r.Get("/", ms.listWebHooks)
|
||||
r.Post("/", ms.postWebHook)
|
||||
r.Get("/{webhook}", ms.getWebHook)
|
||||
r.Put("/{webhook}", ms.putWebHook)
|
||||
r.Delete("/{webhook}", ms.deleteWebHook)
|
||||
})
|
||||
ms.webhooks = WebHooksListResponse{
|
||||
Webhooks: map[string]UrlOrUrls{
|
||||
"new-webhook": {
|
||||
Urls: []string{"http://example.com/new"},
|
||||
},
|
||||
"legacy-webhook": {
|
||||
Url: "http://example.com/legacy",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MockServer) listWebHooks(w http.ResponseWriter, _ *http.Request) {
|
||||
toJSON(w, ms.webhooks)
|
||||
}
|
||||
|
||||
func (ms *MockServer) getWebHook(w http.ResponseWriter, r *http.Request) {
|
||||
resp := WebHookResponse{
|
||||
Webhook: UrlOrUrls{
|
||||
Urls: ms.webhooks.Webhooks[chi.URLParam(r, "webhook")].Urls,
|
||||
},
|
||||
}
|
||||
toJSON(w, resp)
|
||||
}
|
||||
|
||||
func (ms *MockServer) postWebHook(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var urls []string
|
||||
for _, url := range r.Form["url"] {
|
||||
urls = append(urls, url)
|
||||
}
|
||||
ms.webhooks.Webhooks[r.FormValue("id")] = UrlOrUrls{Urls: urls}
|
||||
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) putWebHook(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
toJSON(w, okResp{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var urls []string
|
||||
for _, url := range r.Form["url"] {
|
||||
urls = append(urls, url)
|
||||
}
|
||||
ms.webhooks.Webhooks[chi.URLParam(r, "webhook")] = UrlOrUrls{Urls: urls}
|
||||
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
}
|
||||
|
||||
func (ms *MockServer) deleteWebHook(w http.ResponseWriter, r *http.Request) {
|
||||
_, ok := ms.webhooks.Webhooks[chi.URLParam(r, "webhook")]
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
toJSON(w, okResp{Message: "webhook not found"})
|
||||
}
|
||||
|
||||
delete(ms.webhooks.Webhooks, chi.URLParam(r, "webhook"))
|
||||
toJSON(w, okResp{Message: "success"})
|
||||
}
|
||||
97
vendor/github.com/mailgun/mailgun-go/v4/parse.go
generated
vendored
Normal file
97
vendor/github.com/mailgun/mailgun-go/v4/parse.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
// All events returned by the EventIterator conform to this interface
|
||||
type Event interface {
|
||||
GetName() string
|
||||
SetName(name string)
|
||||
GetTimestamp() time.Time
|
||||
SetTimestamp(time.Time)
|
||||
GetID() string
|
||||
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{}),
|
||||
"complained": new_(events.Complained{}),
|
||||
"delivered": new_(events.Delivered{}),
|
||||
"failed": new_(events.Failed{}),
|
||||
"opened": new_(events.Opened{}),
|
||||
"rejected": new_(events.Rejected{}),
|
||||
"stored": new_(events.Stored{}),
|
||||
"unsubscribed": new_(events.Unsubscribed{}),
|
||||
"list_member_uploaded": new_(events.ListMemberUploaded{}),
|
||||
"list_member_upload_error": new_(events.ListMemberUploadError{}),
|
||||
"list_uploaded": new_(events.ListUploaded{}),
|
||||
}
|
||||
|
||||
// new_ is a universal event "constructor".
|
||||
func new_(e interface{}) func() Event {
|
||||
typ := reflect.TypeOf(e)
|
||||
return func() Event {
|
||||
return reflect.New(typ).Interface().(Event)
|
||||
}
|
||||
}
|
||||
|
||||
func parseResponse(raw []byte) ([]Event, error) {
|
||||
var resp events.Response
|
||||
if err := jsoniter.Unmarshal(raw, &resp); err != nil {
|
||||
return nil, fmt.Errorf("failed to un-marshall event.Response: %s", err)
|
||||
}
|
||||
|
||||
var result []Event
|
||||
for _, value := range resp.Items {
|
||||
event, err := ParseEvent(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while parsing event: %s", err)
|
||||
}
|
||||
result = append(result, event)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Given a slice of events.RawJSON events return a slice of Event for each parsed event
|
||||
func ParseEvents(raw []events.RawJSON) ([]Event, error) {
|
||||
var result []Event
|
||||
for _, value := range raw {
|
||||
event, err := ParseEvent(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while parsing event: %s", err)
|
||||
}
|
||||
result = append(result, event)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Parse converts raw bytes data into an event struct. Can accept events.RawJSON as input
|
||||
func ParseEvent(raw []byte) (Event, error) {
|
||||
// Try to recognize the event first.
|
||||
var e events.EventName
|
||||
if err := jsoniter.Unmarshal(raw, &e); err != nil {
|
||||
return nil, fmt.Errorf("failed to recognize event: %v", err)
|
||||
}
|
||||
|
||||
// Get the event "constructor" from the map.
|
||||
newEvent, ok := EventNames[e.GetName()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported event: '%s'", e.GetName())
|
||||
}
|
||||
event := newEvent()
|
||||
|
||||
// Parse the known event.
|
||||
if err := jsoniter.Unmarshal(raw, event); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse event '%s': %v", e.GetName(), err)
|
||||
}
|
||||
|
||||
return event, nil
|
||||
}
|
||||
42
vendor/github.com/mailgun/mailgun-go/v4/recipients.go
generated
vendored
Normal file
42
vendor/github.com/mailgun/mailgun-go/v4/recipients.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package mailgun
|
||||
|
||||
import "fmt"
|
||||
import "strings"
|
||||
|
||||
type Recipient struct {
|
||||
Name string `json:"-"`
|
||||
Email string `json:"-"`
|
||||
}
|
||||
|
||||
func (r Recipient) String() string {
|
||||
if r.Name != "" {
|
||||
return fmt.Sprintf("%s <%s>", r.Name, r.Email)
|
||||
}
|
||||
return r.Email
|
||||
}
|
||||
|
||||
// MarshalText satisfies TextMarshaler
|
||||
func (r Recipient) MarshalText() ([]byte, error) {
|
||||
return []byte(r.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText satisfies TextUnmarshaler
|
||||
func (r *Recipient) UnmarshalText(text []byte) error {
|
||||
s := string(text)
|
||||
if s[len(s)-1:] != ">" {
|
||||
*r = Recipient{Email: s}
|
||||
return nil
|
||||
}
|
||||
|
||||
i := strings.Index(s, "<")
|
||||
// at least 1 char followed by a space
|
||||
if i < 2 {
|
||||
return fmt.Errorf("malformed recipient string '%s'", s)
|
||||
}
|
||||
*r = Recipient{
|
||||
Name: strings.TrimSpace(s[:i]),
|
||||
Email: s[i+1 : len(s)-1],
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
163
vendor/github.com/mailgun/mailgun-go/v4/rest_shim.go
generated
vendored
Normal file
163
vendor/github.com/mailgun/mailgun-go/v4/rest_shim.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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/" + Version
|
||||
|
||||
// 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
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// String() converts the error into a human-readable, logfmt-compliant string.
|
||||
// See http://godoc.org/github.com/kr/logfmt for details on logfmt formatting.
|
||||
func (e *UnexpectedResponseError) String() string {
|
||||
return fmt.Sprintf("UnexpectedResponseError URL=%s ExpectedOneOf=%#v Got=%d Error: %s", e.URL, e.Expected, e.Actual, string(e.Data))
|
||||
}
|
||||
|
||||
// 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 *httpResponse) error {
|
||||
return &UnexpectedResponseError{
|
||||
URL: url,
|
||||
Expected: expected,
|
||||
Actual: got.Code,
|
||||
Data: got.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, r *httpRequest, method string, p payload) (*httpResponse, error) {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
rsp, err := r.makeRequest(ctx, method, p)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
// getResponseFromJSON shim performs a GET request, checking for a positive outcome.
|
||||
// See simplehttp.GetResponseFromJSON for more details.
|
||||
func getResponseFromJSON(ctx context.Context, r *httpRequest, v interface{}) error {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
response, err := r.makeGetRequest(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response)
|
||||
}
|
||||
return response.parseFromJSON(v)
|
||||
}
|
||||
|
||||
// postResponseFromJSON shim performs a POST request, checking for a positive outcome.
|
||||
// See simplehttp.PostResponseFromJSON for more details.
|
||||
func postResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v interface{}) error {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
response, err := r.makePostRequest(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response)
|
||||
}
|
||||
return response.parseFromJSON(v)
|
||||
}
|
||||
|
||||
// putResponseFromJSON shim performs a PUT request, checking for a positive outcome.
|
||||
// See simplehttp.PutResponseFromJSON for more details.
|
||||
func putResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v interface{}) error {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
response, err := r.makePutRequest(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notGood(response.Code, expected) {
|
||||
return newError(r.URL, expected, response)
|
||||
}
|
||||
return response.parseFromJSON(v)
|
||||
}
|
||||
|
||||
// makeGetRequest shim performs a GET request, checking for a positive outcome.
|
||||
// See simplehttp.MakeGetRequest for more details.
|
||||
func makeGetRequest(ctx context.Context, r *httpRequest) (*httpResponse, error) {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
rsp, err := r.makeGetRequest(ctx)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
// makePostRequest shim performs a POST request, checking for a positive outcome.
|
||||
// See simplehttp.MakePostRequest for more details.
|
||||
func makePostRequest(ctx context.Context, r *httpRequest, p payload) (*httpResponse, error) {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
rsp, err := r.makePostRequest(ctx, p)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
// makePutRequest shim performs a PUT request, checking for a positive outcome.
|
||||
// See simplehttp.MakePutRequest for more details.
|
||||
func makePutRequest(ctx context.Context, r *httpRequest, p payload) (*httpResponse, error) {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
rsp, err := r.makePutRequest(ctx, p)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
// makeDeleteRequest shim performs a DELETE request, checking for a positive outcome.
|
||||
// See simplehttp.MakeDeleteRequest for more details.
|
||||
func makeDeleteRequest(ctx context.Context, r *httpRequest) (*httpResponse, error) {
|
||||
r.addHeader("User-Agent", MailgunGoUserAgent)
|
||||
rsp, err := r.makeDeleteRequest(ctx)
|
||||
if (err == nil) && notGood(rsp.Code, expected) {
|
||||
return rsp, newError(r.URL, expected, rsp)
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
// Extract the http status code from error object
|
||||
func GetStatusFromErr(err error) int {
|
||||
obj, ok := err.(*UnexpectedResponseError)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return obj.Actual
|
||||
}
|
||||
52
vendor/github.com/mailgun/mailgun-go/v4/rfc2822.go
generated
vendored
Normal file
52
vendor/github.com/mailgun/mailgun-go/v4/rfc2822.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Mailgun uses RFC2822 format for timestamps everywhere ('Thu, 13 Oct 2011 18:02:00 GMT'), but
|
||||
// by default Go's JSON package uses another format when decoding/encoding timestamps.
|
||||
type RFC2822Time time.Time
|
||||
|
||||
func NewRFC2822Time(str string) (RFC2822Time, error) {
|
||||
t, err := time.Parse(time.RFC1123, str)
|
||||
if err != nil {
|
||||
return RFC2822Time{}, err
|
||||
}
|
||||
return RFC2822Time(t), nil
|
||||
}
|
||||
|
||||
func (t RFC2822Time) Unix() int64 {
|
||||
return time.Time(t).Unix()
|
||||
}
|
||||
|
||||
func (t RFC2822Time) IsZero() bool {
|
||||
return time.Time(t).IsZero()
|
||||
}
|
||||
|
||||
func (t RFC2822Time) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.Quote(time.Time(t).Format(time.RFC1123))), nil
|
||||
}
|
||||
|
||||
func (t *RFC2822Time) UnmarshalJSON(s []byte) error {
|
||||
q, err := strconv.Unquote(string(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *(*time.Time)(t), err = time.Parse(time.RFC1123, q); err != nil {
|
||||
if strings.Contains(err.Error(), "extra text") {
|
||||
if *(*time.Time)(t), err = time.Parse(time.RFC1123Z, q); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t RFC2822Time) String() string {
|
||||
return time.Time(t).Format(time.RFC1123)
|
||||
}
|
||||
272
vendor/github.com/mailgun/mailgun-go/v4/routes.go
generated
vendored
Normal file
272
vendor/github.com/mailgun/mailgun-go/v4/routes.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Route structure contains information on a configured or to-be-configured 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 {
|
||||
// The Priority field indicates how soon the route works relative to other configured routes.
|
||||
// Routes of equal priority are consulted in chronological order.
|
||||
Priority int `json:"priority,omitempty"`
|
||||
// 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.
|
||||
Description string `json:"description,omitempty"`
|
||||
// The Expression field lets you specify a pattern to match incoming messages against.
|
||||
Expression string `json:"expression,omitempty"`
|
||||
// The Actions field contains strings specifying what to do
|
||||
// with any message which matches the provided expression.
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
|
||||
// The CreatedAt field provides a time-stamp for when the route came into existence.
|
||||
CreatedAt RFC2822Time `json:"created_at,omitempty"`
|
||||
// ID field provides a unique identifier for this route.
|
||||
Id string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type routesListResponse struct {
|
||||
// is -1 if Next() or First() have not been called
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []Route `json:"items"`
|
||||
}
|
||||
|
||||
type createRouteResp struct {
|
||||
Message string `json:"message"`
|
||||
Route `json:"route"`
|
||||
}
|
||||
|
||||
// ListRoutes allows you to iterate through a list of routes returned by the API
|
||||
func (mg *MailgunImpl) ListRoutes(opts *ListOptions) *RoutesIterator {
|
||||
var limit int
|
||||
if opts != nil {
|
||||
limit = opts.Limit
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
return &RoutesIterator{
|
||||
mg: mg,
|
||||
url: generatePublicApiUrl(mg, routesEndpoint),
|
||||
routesListResponse: routesListResponse{TotalCount: -1},
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
type RoutesIterator struct {
|
||||
routesListResponse
|
||||
|
||||
limit int
|
||||
mg Mailgun
|
||||
offset int
|
||||
url string
|
||||
err error
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ri *RoutesIterator) Err() error {
|
||||
return ri.err
|
||||
}
|
||||
|
||||
// Offset returns the current offset of the iterator
|
||||
func (ri *RoutesIterator) Offset() int {
|
||||
return ri.offset
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.err = ri.fetch(ctx, ri.offset, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cpy := make([]Route, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
if len(ri.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
ri.offset = ri.offset + len(ri.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
ri.err = ri.fetch(ctx, 0, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Route, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
ri.offset = len(ri.Items)
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.TotalCount - 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([]Route, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool {
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ri.TotalCount == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
ri.offset = ri.offset - (ri.limit * 2)
|
||||
if ri.offset < 0 {
|
||||
ri.offset = 0
|
||||
}
|
||||
|
||||
ri.err = ri.fetch(ctx, ri.offset, ri.limit)
|
||||
if ri.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Route, len(ri.Items))
|
||||
copy(cpy, ri.Items)
|
||||
*items = cpy
|
||||
if len(ri.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ri *RoutesIterator) 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.routesListResponse)
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, prototype Route) (_ignored Route, err error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, routesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("priority", strconv.Itoa(prototype.Priority))
|
||||
p.addValue("description", prototype.Description)
|
||||
p.addValue("expression", prototype.Expression)
|
||||
for _, action := range prototype.Actions {
|
||||
p.addValue("action", action)
|
||||
}
|
||||
var resp createRouteResp
|
||||
if err = postResponseFromJSON(ctx, r, p, &resp); err != nil {
|
||||
return _ignored, err
|
||||
}
|
||||
return resp.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(ctx context.Context, id string) error {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, routesEndpoint) + "/" + id)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetRoute retrieves the complete route definition associated with the unique route ID.
|
||||
func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (Route, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, routesEndpoint) + "/" + id)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var envelope struct {
|
||||
Message string `json:"message"`
|
||||
*Route `json:"route"`
|
||||
}
|
||||
err := getResponseFromJSON(ctx, r, &envelope)
|
||||
if err != nil {
|
||||
return Route{}, err
|
||||
}
|
||||
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(ctx context.Context, id string, route Route) (Route, error) {
|
||||
r := newHTTPRequest(generatePublicApiUrl(mg, routesEndpoint) + "/" + id)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := 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(ctx, r, p, &envelope)
|
||||
return envelope, err
|
||||
}
|
||||
174
vendor/github.com/mailgun/mailgun-go/v4/spam_complaints.go
generated
vendored
Normal file
174
vendor/github.com/mailgun/mailgun-go/v4/spam_complaints.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
complaintsEndpoint = "complaints"
|
||||
)
|
||||
|
||||
// Complaint structures track how many times one of your emails have been marked as spam.
|
||||
// the recipient thought your messages were not solicited.
|
||||
type Complaint struct {
|
||||
Count int `json:"count"`
|
||||
CreatedAt RFC2822Time `json:"created_at"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type complaintsResponse struct {
|
||||
Paging Paging `json:"paging"`
|
||||
Items []Complaint `json:"items"`
|
||||
}
|
||||
|
||||
// ListComplaints 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 (mg *MailgunImpl) ListComplaints(opts *ListOptions) *ComplaintsIterator {
|
||||
r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &ComplaintsIterator{
|
||||
mg: mg,
|
||||
complaintsResponse: complaintsResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type ComplaintsIterator struct {
|
||||
complaintsResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ci *ComplaintsIterator) Err() error {
|
||||
return ci.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Next)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Complaint, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.First)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Complaint, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Last)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Complaint, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]Complaint) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
if ci.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Previous)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Complaint, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ci *ComplaintsIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(ci.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, ci.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &ci.complaintsResponse)
|
||||
}
|
||||
|
||||
// GetComplaint 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 (mg *MailgunImpl) GetComplaint(ctx context.Context, address string) (Complaint, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint) + "/" + address)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var c Complaint
|
||||
err := getResponseFromJSON(ctx, r, &c)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// CreateComplaint registers the specified address as a recipient who has complained of receiving spam
|
||||
// from your domain.
|
||||
func (mg *MailgunImpl) CreateComplaint(ctx context.Context, address string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("address", address)
|
||||
_, err := makePostRequest(ctx, 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 (mg *MailgunImpl) DeleteComplaint(ctx context.Context, address string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, complaintsEndpoint) + "/" + address)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
120
vendor/github.com/mailgun/mailgun-go/v4/stats.go
generated
vendored
Normal file
120
vendor/github.com/mailgun/mailgun-go/v4/stats.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
SuppressComplaint int `json:"suppress-complaint"`
|
||||
Bounce int `json:"bounce"`
|
||||
DelayedBounce int `json:"delayed-bounce"`
|
||||
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"`
|
||||
Delivered Delivered `json:"delivered"`
|
||||
Failed Failed `json:"failed"`
|
||||
Stored Total `json:"stored"`
|
||||
Opened Total `json:"opened"`
|
||||
Clicked Total `json:"clicked"`
|
||||
Unsubscribed Total `json:"unsubscribed"`
|
||||
Complained Total `json:"complained"`
|
||||
}
|
||||
|
||||
type statsTotalResponse struct {
|
||||
End string `json:"end"`
|
||||
Resolution string `json:"resolution"`
|
||||
Start string `json:"start"`
|
||||
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")
|
||||
)
|
||||
|
||||
// Options for GetStats()
|
||||
type GetStatOptions struct {
|
||||
Resolution Resolution
|
||||
Duration string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
// GetStats returns total stats for a given domain for the specified time period
|
||||
func (mg *MailgunImpl) GetStats(ctx context.Context, events []string, opts *GetStatOptions) ([]Stats, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, statsTotalEndpoint))
|
||||
|
||||
if opts != nil {
|
||||
if !opts.Start.IsZero() {
|
||||
r.addParameter("start", strconv.Itoa(int(opts.Start.Unix())))
|
||||
}
|
||||
if !opts.End.IsZero() {
|
||||
r.addParameter("end", strconv.Itoa(int(opts.End.Unix())))
|
||||
}
|
||||
if opts.Resolution != "" {
|
||||
r.addParameter("resolution", string(opts.Resolution))
|
||||
}
|
||||
if opts.Duration != "" {
|
||||
r.addParameter("duration", opts.Duration)
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range events {
|
||||
r.addParameter("event", e)
|
||||
}
|
||||
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var res statsTotalResponse
|
||||
err := getResponseFromJSON(ctx, r, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return res.Stats, nil
|
||||
}
|
||||
}
|
||||
183
vendor/github.com/mailgun/mailgun-go/v4/tags.go
generated
vendored
Normal file
183
vendor/github.com/mailgun/mailgun-go/v4/tags.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Tag struct {
|
||||
Value string `json:"tag"`
|
||||
Description string `json:"description"`
|
||||
FirstSeen *time.Time `json:"first-seen,omitempty"`
|
||||
LastSeen *time.Time `json:"last-seen,omitempty"`
|
||||
}
|
||||
|
||||
type tagsResponse struct {
|
||||
Items []Tag `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
type ListTagOptions struct {
|
||||
// Restrict the page size to this limit
|
||||
Limit int
|
||||
// Return only the tags starting with the given prefix
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// DeleteTag removes all counters for a particular tag, including the tag itself.
|
||||
func (mg *MailgunImpl) DeleteTag(ctx context.Context, tag string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, tagsEndpoint) + "/" + tag)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTag retrieves metadata about the tag from the api
|
||||
func (mg *MailgunImpl) GetTag(ctx context.Context, tag string) (Tag, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, tagsEndpoint) + "/" + tag)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var tagItem Tag
|
||||
return tagItem, getResponseFromJSON(ctx, r, &tagItem)
|
||||
}
|
||||
|
||||
// ListTags returns a cursor used to iterate through a list of tags
|
||||
// it := mg.ListTags(nil)
|
||||
// var page []mailgun.Tag
|
||||
// for it.Next(&page) {
|
||||
// for _, tag := range(page) {
|
||||
// // Do stuff with tags
|
||||
// }
|
||||
// }
|
||||
// if it.Err() != nil {
|
||||
// log.Fatal(it.Err())
|
||||
// }
|
||||
func (mg *MailgunImpl) ListTags(opts *ListTagOptions) *TagIterator {
|
||||
req := newHTTPRequest(generateApiUrl(mg, tagsEndpoint))
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
req.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
if opts.Prefix != "" {
|
||||
req.addParameter("prefix", opts.Prefix)
|
||||
}
|
||||
}
|
||||
|
||||
url, err := req.generateUrlWithParameters()
|
||||
return &TagIterator{
|
||||
tagsResponse: tagsResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
mg: mg,
|
||||
}
|
||||
}
|
||||
|
||||
type TagIterator struct {
|
||||
tagsResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// Next returns the next page in the list of tags
|
||||
func (ti *TagIterator) Next(ctx context.Context, items *[]Tag) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !canFetchPage(ti.Paging.Next) {
|
||||
return false
|
||||
}
|
||||
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Next)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = ti.Items
|
||||
if len(ti.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous returns the previous page in the list of tags
|
||||
func (ti *TagIterator) Previous(ctx context.Context, items *[]Tag) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ti.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !canFetchPage(ti.Paging.Previous) {
|
||||
return false
|
||||
}
|
||||
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Previous)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = ti.Items
|
||||
if len(ti.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First returns the first page in the list of tags
|
||||
func (ti *TagIterator) First(ctx context.Context, items *[]Tag) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.First)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = ti.Items
|
||||
return true
|
||||
}
|
||||
|
||||
// Last returns the last page in the list of tags
|
||||
func (ti *TagIterator) Last(ctx context.Context, items *[]Tag) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Last)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
*items = ti.Items
|
||||
return true
|
||||
}
|
||||
|
||||
// Err returns any error if one occurred
|
||||
func (ti *TagIterator) Err() error {
|
||||
return ti.err
|
||||
}
|
||||
|
||||
func (ti *TagIterator) fetch(ctx context.Context, url string) error {
|
||||
req := newHTTPRequest(url)
|
||||
req.setClient(ti.mg.Client())
|
||||
req.setBasicAuth(basicAuthUser, ti.mg.APIKey())
|
||||
return getResponseFromJSON(ctx, req, &ti.tagsResponse)
|
||||
}
|
||||
|
||||
func canFetchPage(slug string) bool {
|
||||
parts, err := url.Parse(slug)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
params, _ := url.ParseQuery(parts.RawQuery)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
value, ok := params["tag"]
|
||||
// If tags doesn't exist, it's our first time fetching pages
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
// If tags has no value, there are no more pages to fetch
|
||||
return len(value) == 0
|
||||
}
|
||||
243
vendor/github.com/mailgun/mailgun-go/v4/template.go
generated
vendored
Normal file
243
vendor/github.com/mailgun/mailgun-go/v4/template.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TemplateEngine string
|
||||
|
||||
// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine
|
||||
const (
|
||||
TemplateEngineMustache = TemplateEngine("mustache")
|
||||
TemplateEngineHandlebars = TemplateEngine("handlebars")
|
||||
TemplateEngineGo = TemplateEngine("go")
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt RFC2822Time `json:"createdAt"`
|
||||
Version TemplateVersion `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type templateResp struct {
|
||||
Item Template `json:"template"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type templateListResp struct {
|
||||
Items []Template `json:"items"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
// 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())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
|
||||
if template.Name != "" {
|
||||
payload.addValue("name", template.Name)
|
||||
}
|
||||
if template.Description != "" {
|
||||
payload.addValue("description", template.Description)
|
||||
}
|
||||
|
||||
if template.Version.Engine != "" {
|
||||
payload.addValue("engine", string(template.Version.Engine))
|
||||
}
|
||||
if template.Version.Template != "" {
|
||||
payload.addValue("template", template.Version.Template)
|
||||
}
|
||||
if template.Version.Comment != "" {
|
||||
payload.addValue("comment", template.Version.Comment)
|
||||
}
|
||||
if template.Version.Tag != "" {
|
||||
payload.addValue("tag", template.Version.Tag)
|
||||
}
|
||||
|
||||
var resp templateResp
|
||||
if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
*template = resp.Item
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTemplate gets a template given the template name
|
||||
func (mg *MailgunImpl) GetTemplate(ctx context.Context, name string) (Template, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + name)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
r.addParameter("active", "yes")
|
||||
|
||||
var resp templateResp
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
if err != nil {
|
||||
return Template{}, err
|
||||
}
|
||||
return resp.Item, nil
|
||||
}
|
||||
|
||||
// Update the name and description of a template
|
||||
func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, template *Template) error {
|
||||
if template.Name == "" {
|
||||
return errors.New("UpdateTemplate() Template.Name cannot be empty")
|
||||
}
|
||||
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + template.Name)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
|
||||
if template.Name != "" {
|
||||
p.addValue("name", template.Name)
|
||||
}
|
||||
if template.Description != "" {
|
||||
p.addValue("description", template.Description)
|
||||
}
|
||||
|
||||
var resp templateResp
|
||||
err := putResponseFromJSON(ctx, r, p, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*template = resp.Item
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete a template given a template name
|
||||
func (mg *MailgunImpl) DeleteTemplate(ctx context.Context, name string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + name)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
type TemplatesIterator struct {
|
||||
templateListResp
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
type ListTemplateOptions struct {
|
||||
Limit int
|
||||
Active bool
|
||||
}
|
||||
|
||||
// List all available templates
|
||||
func (mg *MailgunImpl) ListTemplates(opts *ListTemplateOptions) *TemplatesIterator {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
if opts.Active {
|
||||
r.addParameter("active", "yes")
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &TemplatesIterator{
|
||||
mg: mg,
|
||||
templateListResp: templateListResp{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ti *TemplatesIterator) Err() error {
|
||||
return ti.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Next)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Template, len(ti.Items))
|
||||
copy(cpy, ti.Items)
|
||||
*items = cpy
|
||||
if len(ti.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.First)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Template, len(ti.Items))
|
||||
copy(cpy, ti.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Last)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Template, len(ti.Items))
|
||||
copy(cpy, ti.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]Template) bool {
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
if ti.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
ti.err = ti.fetch(ctx, ti.Paging.Previous)
|
||||
if ti.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Template, len(ti.Items))
|
||||
copy(cpy, ti.Items)
|
||||
*items = cpy
|
||||
if len(ti.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ti *TemplatesIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(ti.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, ti.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &ti.templateListResp)
|
||||
}
|
||||
217
vendor/github.com/mailgun/mailgun-go/v4/template_versions.go
generated
vendored
Normal file
217
vendor/github.com/mailgun/mailgun-go/v4/template_versions.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TemplateVersion struct {
|
||||
Tag string `json:"tag"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Engine TemplateEngine `json:"engine"`
|
||||
CreatedAt RFC2822Time `json:"createdAt"`
|
||||
Comment string `json:"comment"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
type templateVersionListResp struct {
|
||||
Template struct {
|
||||
Template
|
||||
Versions []TemplateVersion `json:"versions,omitempty"`
|
||||
} `json:"template"`
|
||||
Paging Paging `json:"paging"`
|
||||
}
|
||||
|
||||
// AddTemplateVersion adds a template version to a template
|
||||
func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, templateName string, version *TemplateVersion) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateName + "/versions")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
payload := newUrlEncodedPayload()
|
||||
payload.addValue("template", version.Template)
|
||||
|
||||
if version.Tag != "" {
|
||||
payload.addValue("tag", string(version.Tag))
|
||||
}
|
||||
if version.Engine != "" {
|
||||
payload.addValue("engine", string(version.Engine))
|
||||
}
|
||||
if version.Comment != "" {
|
||||
payload.addValue("comment", version.Comment)
|
||||
}
|
||||
if version.Active {
|
||||
payload.addValue("active", boolToString(version.Active))
|
||||
}
|
||||
|
||||
var resp templateResp
|
||||
if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
*version = resp.Item.Version
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTemplateVersion gets a specific version of a template
|
||||
func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, templateName, tag string) (TemplateVersion, error) {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateName + "/versions/" + tag)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var resp templateResp
|
||||
err := getResponseFromJSON(ctx, r, &resp)
|
||||
if err != nil {
|
||||
return TemplateVersion{}, err
|
||||
}
|
||||
return resp.Item.Version, nil
|
||||
}
|
||||
|
||||
// Update the comment and mark a version of a template active
|
||||
func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, templateName string, version *TemplateVersion) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateName + "/versions/" + version.Tag)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
|
||||
if version.Comment != "" {
|
||||
p.addValue("comment", version.Comment)
|
||||
}
|
||||
if version.Active {
|
||||
p.addValue("active", boolToString(version.Active))
|
||||
}
|
||||
if version.Template != "" {
|
||||
p.addValue("template", version.Template)
|
||||
}
|
||||
|
||||
var resp templateResp
|
||||
err := putResponseFromJSON(ctx, r, p, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*version = resp.Item.Version
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete a specific version of a template
|
||||
func (mg *MailgunImpl) DeleteTemplateVersion(ctx context.Context, templateName, tag string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateName + "/versions/" + tag)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
type TemplateVersionsIterator struct {
|
||||
templateVersionListResp
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// List all the versions of a specific template
|
||||
func (mg *MailgunImpl) ListTemplateVersions(templateName string, opts *ListOptions) *TemplateVersionsIterator {
|
||||
r := newHTTPRequest(generateApiUrl(mg, templatesEndpoint) + "/" + templateName + "/versions")
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &TemplateVersionsIterator{
|
||||
mg: mg,
|
||||
templateVersionListResp: templateVersionListResp{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (li *TemplateVersionsIterator) Err() error {
|
||||
return li.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateVersion) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Next)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]TemplateVersion, len(li.Template.Versions))
|
||||
copy(cpy, li.Template.Versions)
|
||||
*items = cpy
|
||||
if len(li.Template.Versions) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]TemplateVersion) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.First)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]TemplateVersion, len(li.Template.Versions))
|
||||
copy(cpy, li.Template.Versions)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateVersion) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Last)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]TemplateVersion, len(li.Template.Versions))
|
||||
copy(cpy, li.Template.Versions)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]TemplateVersion) bool {
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
if li.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
li.err = li.fetch(ctx, li.Paging.Previous)
|
||||
if li.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]TemplateVersion, len(li.Template.Versions))
|
||||
copy(cpy, li.Template.Versions)
|
||||
*items = cpy
|
||||
if len(li.Template.Versions) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (li *TemplateVersionsIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(li.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, li.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &li.templateVersionListResp)
|
||||
}
|
||||
180
vendor/github.com/mailgun/mailgun-go/v4/unsubscribes.go
generated
vendored
Normal file
180
vendor/github.com/mailgun/mailgun-go/v4/unsubscribes.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Unsubscribe struct {
|
||||
CreatedAt RFC2822Time `json:"created_at"`
|
||||
Tags []string `json:"tags"`
|
||||
ID string `json:"id"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type unsubscribesResponse struct {
|
||||
Paging Paging `json:"paging"`
|
||||
Items []Unsubscribe `json:"items"`
|
||||
}
|
||||
|
||||
// Fetches the list of unsubscribes
|
||||
func (mg *MailgunImpl) ListUnsubscribes(opts *ListOptions) *UnsubscribesIterator {
|
||||
r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
if opts != nil {
|
||||
if opts.Limit != 0 {
|
||||
r.addParameter("limit", strconv.Itoa(opts.Limit))
|
||||
}
|
||||
}
|
||||
url, err := r.generateUrlWithParameters()
|
||||
return &UnsubscribesIterator{
|
||||
mg: mg,
|
||||
unsubscribesResponse: unsubscribesResponse{Paging: Paging{Next: url, First: url}},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type UnsubscribesIterator struct {
|
||||
unsubscribesResponse
|
||||
mg Mailgun
|
||||
err error
|
||||
}
|
||||
|
||||
// If an error occurred during iteration `Err()` will return non nil
|
||||
func (ci *UnsubscribesIterator) Err() error {
|
||||
return ci.err
|
||||
}
|
||||
|
||||
// Next retrieves the next page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error
|
||||
func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Next)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Unsubscribe, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First retrieves the first page of items from the api. Returns false if there
|
||||
// was an error. It also sets the iterator object to the first page.
|
||||
// Use `.Err()` to retrieve the error.
|
||||
func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.First)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Unsubscribe, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Last retrieves the last page of items from the api.
|
||||
// Calling Last() is invalid unless you first call First() or Next()
|
||||
// Returns false if there was an error. It also sets the iterator object
|
||||
// to the last page. Use `.Err()` to retrieve the error.
|
||||
func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Last)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Unsubscribe, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
return true
|
||||
}
|
||||
|
||||
// Previous retrieves the previous page of items from the api. Returns false when there
|
||||
// no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
|
||||
// the error if any
|
||||
func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]Unsubscribe) bool {
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
if ci.Paging.Previous == "" {
|
||||
return false
|
||||
}
|
||||
ci.err = ci.fetch(ctx, ci.Paging.Previous)
|
||||
if ci.err != nil {
|
||||
return false
|
||||
}
|
||||
cpy := make([]Unsubscribe, len(ci.Items))
|
||||
copy(cpy, ci.Items)
|
||||
*items = cpy
|
||||
if len(ci.Items) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ci *UnsubscribesIterator) fetch(ctx context.Context, url string) error {
|
||||
r := newHTTPRequest(url)
|
||||
r.setClient(ci.mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, ci.mg.APIKey())
|
||||
|
||||
return getResponseFromJSON(ctx, r, &ci.unsubscribesResponse)
|
||||
}
|
||||
|
||||
// Retreives a single unsubscribe record. Can be used to check if a given address is present in the list of unsubscribed users.
|
||||
func (mg *MailgunImpl) GetUnsubscribe(ctx context.Context, address string) (Unsubscribe, error) {
|
||||
r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, address))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
envelope := Unsubscribe{}
|
||||
err := getResponseFromJSON(ctx, r, &envelope)
|
||||
|
||||
return envelope, err
|
||||
}
|
||||
|
||||
// Unsubscribe adds an e-mail address to the domain's unsubscription table.
|
||||
func (mg *MailgunImpl) CreateUnsubscribe(ctx context.Context, address, tag string) error {
|
||||
r := newHTTPRequest(generateApiUrl(mg, unsubscribesEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("address", address)
|
||||
p.addValue("tag", tag)
|
||||
_, err := makePostRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUnsubscribe removes the e-mail address given from the domain's unsubscription table.
|
||||
// If passing in an ID (discoverable from, e.g., ListUnsubscribes()), the e-mail address associated
|
||||
// with the given ID will be removed.
|
||||
func (mg *MailgunImpl) DeleteUnsubscribe(ctx context.Context, address string) error {
|
||||
r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, address))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUnsubscribeWithTag removes the e-mail address given from the domain's unsubscription table with a matching tag.
|
||||
// If passing in an ID (discoverable from, e.g., ListUnsubscribes()), the e-mail address associated
|
||||
// with the given ID will be removed.
|
||||
func (mg *MailgunImpl) DeleteUnsubscribeWithTag(ctx context.Context, a, t string) error {
|
||||
r := newHTTPRequest(generateApiUrlWithTarget(mg, unsubscribesEndpoint, a))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
r.addParameter("tag", t)
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
4
vendor/github.com/mailgun/mailgun-go/v4/version.go
generated
vendored
Normal file
4
vendor/github.com/mailgun/mailgun-go/v4/version.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package mailgun
|
||||
|
||||
// Version of current release
|
||||
const Version = "4.2.0"
|
||||
157
vendor/github.com/mailgun/mailgun-go/v4/webhooks.go
generated
vendored
Normal file
157
vendor/github.com/mailgun/mailgun-go/v4/webhooks.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/mailgun/mailgun-go/v4/events"
|
||||
)
|
||||
|
||||
type UrlOrUrls struct {
|
||||
Urls []string `json:"urls"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type WebHooksListResponse struct {
|
||||
Webhooks map[string]UrlOrUrls `json:"webhooks"`
|
||||
}
|
||||
|
||||
type WebHookResponse struct {
|
||||
Webhook UrlOrUrls `json:"webhook"`
|
||||
}
|
||||
|
||||
// ListWebhooks returns the complete set of webhooks configured for your domain.
|
||||
// Note that a zero-length mapping is not an error.
|
||||
func (mg *MailgunImpl) ListWebhooks(ctx context.Context) (map[string][]string, error) {
|
||||
r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
|
||||
var body WebHooksListResponse
|
||||
err := getResponseFromJSON(ctx, r, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hooks := make(map[string][]string, 0)
|
||||
for k, v := range body.Webhooks {
|
||||
if v.Url != "" {
|
||||
hooks[k] = []string{v.Url}
|
||||
}
|
||||
if len(v.Urls) != 0 {
|
||||
hooks[k] = append(hooks[k], v.Urls...)
|
||||
}
|
||||
}
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
// CreateWebhook installs a new webhook for your domain.
|
||||
func (mg *MailgunImpl) CreateWebhook(ctx context.Context, kind string, urls []string) error {
|
||||
r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint))
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
p.addValue("id", kind)
|
||||
for _, url := range urls {
|
||||
p.addValue("url", url)
|
||||
}
|
||||
_, err := makePostRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteWebhook removes the specified webhook from your domain's configuration.
|
||||
func (mg *MailgunImpl) DeleteWebhook(ctx context.Context, kind string) error {
|
||||
r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + kind)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
_, err := makeDeleteRequest(ctx, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWebhook retrieves the currently assigned webhook URL associated with the provided type of webhook.
|
||||
func (mg *MailgunImpl) GetWebhook(ctx context.Context, kind string) ([]string, error) {
|
||||
r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + kind)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
var body WebHookResponse
|
||||
if err := getResponseFromJSON(ctx, r, &body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if body.Webhook.Url != "" {
|
||||
return []string{body.Webhook.Url}, nil
|
||||
}
|
||||
if len(body.Webhook.Urls) != 0 {
|
||||
return body.Webhook.Urls, nil
|
||||
}
|
||||
return nil, fmt.Errorf("webhook '%s' returned no urls", kind)
|
||||
}
|
||||
|
||||
// UpdateWebhook replaces one webhook setting for another.
|
||||
func (mg *MailgunImpl) UpdateWebhook(ctx context.Context, kind string, urls []string) error {
|
||||
r := newHTTPRequest(generateDomainApiUrl(mg, webhooksEndpoint) + "/" + kind)
|
||||
r.setClient(mg.Client())
|
||||
r.setBasicAuth(basicAuthUser, mg.APIKey())
|
||||
p := newUrlEncodedPayload()
|
||||
for _, url := range urls {
|
||||
p.addValue("url", url)
|
||||
}
|
||||
_, err := makePutRequest(ctx, r, p)
|
||||
return err
|
||||
}
|
||||
|
||||
// Represents the signature portion of the webhook POST body
|
||||
type Signature struct {
|
||||
TimeStamp string `json:"timestamp"`
|
||||
Token string `json:"token"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// Represents the JSON payload provided when a Webhook is called by mailgun
|
||||
type WebhookPayload struct {
|
||||
Signature Signature `json:"signature"`
|
||||
EventData events.RawJSON `json:"event-data"`
|
||||
}
|
||||
|
||||
// Use this method to parse the webhook signature given as JSON in the webhook response
|
||||
func (mg *MailgunImpl) VerifyWebhookSignature(sig Signature) (verified bool, err error) {
|
||||
h := hmac.New(sha256.New, []byte(mg.APIKey()))
|
||||
io.WriteString(h, sig.TimeStamp)
|
||||
io.WriteString(h, sig.Token)
|
||||
|
||||
calculatedSignature := h.Sum(nil)
|
||||
signature, err := hex.DecodeString(sig.Signature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(calculatedSignature) != len(signature) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return subtle.ConstantTimeCompare(signature, calculatedSignature) == 1, nil
|
||||
}
|
||||
|
||||
// Deprecated: Please use the VerifyWebhookSignature() to parse the latest
|
||||
// version of WebHooks from mailgun
|
||||
func (mg *MailgunImpl) VerifyWebhookRequest(req *http.Request) (verified bool, err error) {
|
||||
h := hmac.New(sha256.New, []byte(mg.APIKey()))
|
||||
io.WriteString(h, req.FormValue("timestamp"))
|
||||
io.WriteString(h, req.FormValue("token"))
|
||||
|
||||
calculatedSignature := h.Sum(nil)
|
||||
signature, err := hex.DecodeString(req.FormValue("signature"))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(calculatedSignature) != len(signature) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return subtle.ConstantTimeCompare(signature, calculatedSignature) == 1, nil
|
||||
}
|
||||
Reference in New Issue
Block a user