Skip to content

Commit

Permalink
Merge pull request #165 from thrawn01/fix-get-stored
Browse files Browse the repository at this point in the history
GetStoredMessage() now accepts a URL instead of an id
  • Loading branch information
thrawn01 authored Feb 12, 2019
2 parents f473727 + a4621ca commit f987588
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 135 deletions.
16 changes: 0 additions & 16 deletions .env

This file was deleted.

8 changes: 7 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### 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()

### Added
* Added `GetStoredAttachmentForURL()`
* Added `GetStoredAttachment()`

### Removed
* Method `DeleteStoredMessage()` mailgun API no long allows this call

## [3.3.0] - 2019-01-28
### Changes
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/mailgun/mailgun-go/v3

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1
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
Expand Down
8 changes: 5 additions & 3 deletions mailgun.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,14 @@ type Mailgun interface {
GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error)
GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error)

GetStoredMessage(ctx context.Context, id string) (StoredMessage, 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)
GetStoredAttachmentForURL(ctx context.Context, url string) ([]byte, error)
DeleteStoredMessage(ctx context.Context, id string) error

ListCredentials(opts *ListOptions) *CredentialsIterator
CreateCredential(ctx context.Context, login, password string) error
Expand Down
50 changes: 9 additions & 41 deletions messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,7 @@ func validateStringList(list []string, requireOne bool) bool {

// 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, id string) (StoredMessage, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, url string) (StoredMessage, error) {
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.APIKey())
Expand All @@ -700,8 +699,7 @@ func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, id string) (StoredM
}

// Given a storage id resend the stored message to the specified recipients
func (mg *MailgunImpl) ReSend(ctx context.Context, storageURL string, recipients ...string) (string, string, error) {
url := generateDomainApiUrl(mg, messagesEndpoint) + "/" + storageURL
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())
Expand All @@ -728,8 +726,7 @@ func (mg *MailgunImpl) ReSend(ctx context.Context, storageURL string, recipients
// 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, id string) (StoredMessageRaw, error) {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, url string) (StoredMessageRaw, error) {
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.APIKey())
Expand All @@ -740,47 +737,18 @@ func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, id string) (Stor
return response, err
}

// GetStoredMessageForURL retrieves information about a received e-mail message.
// This provides visibility into, e.g., replies to a message sent to a mailing list.
// Deprecated: Use GetStoreMessage() instead
func (mg *MailgunImpl) GetStoredMessageForURL(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
return mg.GetStoredMessage(ctx, url)
}

// GetStoredMessageRawForURL 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.
// Deprecated: Use GetStoreMessageRaw() instead
func (mg *MailgunImpl) GetStoredMessageRawForURL(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

}

// DeleteStoredMessage removes a previously stored message.
// Note that Mailgun institutes a policy of automatically deleting messages after a set time.
// Consult the current Mailgun API documentation for more details.
func (mg *MailgunImpl) DeleteStoredMessage(ctx context.Context, id string) error {
url := generateStoredMessageUrl(mg, messagesEndpoint, id)
r := newHTTPRequest(url)
r.setClient(mg.Client())
r.setBasicAuth(basicAuthUser, mg.APIKey())
_, err := makeDeleteRequest(ctx, r)
return err
return mg.GetStoredMessageRaw(ctx, url)
}

// GetStoredAttachmentForURL retrieves the raw MIME body of a received e-mail message attachment.
func (mg *MailgunImpl) GetStoredAttachmentForURL(ctx context.Context, url string) ([]byte, error) {
// 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())
Expand Down
61 changes: 3 additions & 58 deletions messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"time"

"github.com/facebookgo/ensure"
"github.com/mailgun/mailgun-go/v3/events"
)

const (
Expand All @@ -35,60 +34,6 @@ Testing some Mailgun MIME awesomeness!
exampleAPIKey = "testAPIKey"
)

func TestGetStoredMessage(t *testing.T) {
if reason := SkipNetworkTest(); reason != "" {
t.Skip(reason)
}

spendMoney(t, func() {
mg, err := NewMailgunFromEnv()
ensure.Nil(t, err)

id, err := findStoredMessageID(mg) // somehow...
if err != nil {
t.Log(err)
return
}

ctx := context.Background()
// First, get our stored message.
msg, err := mg.GetStoredMessage(ctx, id)
ensure.Nil(t, err)

fields := map[string]string{
" From": msg.From,
" Sender": msg.Sender,
" Subject": msg.Subject,
"Attachments": fmt.Sprintf("%d", len(msg.Attachments)),
" Headers": fmt.Sprintf("%d", len(msg.MessageHeaders)),
}
for k, v := range fields {
fmt.Printf("%13s: %s\n", k, v)
}

// We're done with it; now delete it.
ensure.Nil(t, mg.DeleteStoredMessage(ctx, id))
})
}

// Tries to locate the first stored event type, returning the associated stored message key.
func findStoredMessageID(mg Mailgun) (string, error) {
it := mg.ListEvents(nil)

var page []Event
for it.Next(context.Background(), &page) {
for _, event := range page {
if event.GetName() == events.EventStored {
return event.(*events.Stored).Storage.Key, nil
}
}
}
if it.Err() != nil {
return "", it.Err()
}
return "", fmt.Errorf("No stored messages found. Try changing MG_EMAIL_TO to an address that stores messages and try again.")
}

func TestSendMGPlain(t *testing.T) {
if reason := SkipNetworkTest(); reason != "" {
t.Skip(reason)
Expand Down Expand Up @@ -478,7 +423,7 @@ func TestResendStored(t *testing.T) {
)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ensure.DeepEqual(t, req.Method, http.MethodPost)
ensure.DeepEqual(t, req.URL.Path, fmt.Sprintf("/domains/%s/messages/some-url", exampleDomain))
ensure.DeepEqual(t, req.URL.Path, "/some-url")
ensure.DeepEqual(t, req.FormValue("to"), toUser)

rsp := fmt.Sprintf(`{"message":"%s", "id":"%s"}`, exampleMessage, exampleID)
Expand All @@ -489,11 +434,11 @@ func TestResendStored(t *testing.T) {
mg := NewMailgun(exampleDomain, exampleAPIKey)
mg.SetAPIBase(srv.URL)

msg, id, err := mg.ReSend(context.Background(), "some-url")
msg, id, err := mg.ReSend(context.Background(), srv.URL+"/some-url")
ensure.NotNil(t, err)
ensure.DeepEqual(t, err.Error(), "must provide at least one recipient")

msg, id, err = mg.ReSend(context.Background(), "some-url", toUser)
msg, id, err = mg.ReSend(context.Background(), srv.URL+"/some-url", toUser)
ensure.Nil(t, err)
ensure.DeepEqual(t, msg, exampleMessage)
ensure.DeepEqual(t, id, exampleID)
Expand Down
110 changes: 95 additions & 15 deletions mock_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import (

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
Expand All @@ -22,22 +26,98 @@ func (ms *MockServer) createMessages(w http.ResponseWriter, r *http.Request) {
toJSON(w, okResp{Message: "invalid 'to' address"})
return
}
id := randomString(16, "ID-")

switch to.Address {
case "[email protected]":
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)
}
}

accepted := new(events.Accepted)
accepted.ID = randomString(16, "ID-")
accepted.Name = events.EventAccepted
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,
if stored == nil {
w.WriteHeader(http.StatusNotFound)
toJSON(w, okResp{Message: "not found"})
}
ms.events = append(ms.events, accepted)

toJSON(w, okResp{ID: "<" + accepted.ID + ">", Message: "Queued. Thank you."})
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."})
}
Loading

0 comments on commit f987588

Please sign in to comment.