Git Product home page Git Product logo

mailgun-go's Introduction

Mailgun with Go

GoDoc Build Status

Go library for interacting with the Mailgun API.

Usage

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)
	
	//When you have an EU-domain, you must specify the endpoint:
	//mg.SetAPIBase("https://api.eu.mailgun.net/v3")

	sender := "[email protected]"
	subject := "Fancy subject!"
	body := "Hello from Mailgun Go!"
	recipient := "[email protected]"

	// 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

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

package main

import (
	"context"
	"log"
	"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 {
			log.Printf("Got an event: %q (%q)", e.GetName(), e.GetID())
			// Do something with event
		}
	}
}

Email Validations

package main

import (
	"context"
	"fmt"
	"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, "[email protected]", false)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Valid: %t\n", email.IsValid)
}

Webhook Handling

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"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)
	}
}

Sending HTML templates

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 := "[email protected]"
	subject := "HTML email!"
	recipient := "[email protected]"

	message := mg.NewMessage(sender, subject, "", recipient)
	body := `
<html>
<body>
	<h1>Sending HTML emails with Mailgun</h1>
	<p style="color:blue; font-size:30px;">Hello world</p>
	<p style="font-size:30px;">More examples can be found <a href="https://documentation.mailgun.com/en/latest/api-sending.html#examples">here</a></p>
</body>
</html>
`

	message.SetHtml(body)

	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)
}

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:

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 := "[email protected]"
	subject := "Fancy subject!"
	body := ""
	recipient := "[email protected]"

	// The message object allows you to add attachments and Bcc recipients
	message := mg.NewMessage(sender, subject, body, recipient)
	message.SetTemplate("passwordReset")
	err := message.AddTemplateVariable("passwordResetLink", "some link to your site unique to your user")
	if err != nil {
		log.Fatal(err)
	}

	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 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

mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg.SetAPIBase(mailgun.APIBaseEU)

Installation

If you are using golang modules make sure you include the /v4 at the end of your import paths

$ 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.

$ 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 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.

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
  • MG_API_KEY is the Private API key - you can get this value from the Mailgun security page
  • 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.

mailgun-go's People

Contributors

b0d0nne11 avatar brynbellomy avatar burritobowl avatar c2h5oh avatar danieldias51 avatar dellalu avatar dratner avatar guessi avatar heltonmarx avatar hownowstephen avatar james-lawrence avatar kevineaton avatar kevinetienne avatar khanhhongpham avatar kshvakov avatar loicmahieu avatar matthewvalimaki avatar mbanzon avatar mfridman avatar mvisonneau avatar rasky avatar reddec avatar robbiet480 avatar sam-falvo avatar shaumux avatar teodorofilippini avatar teros0 avatar tgallice avatar thrawn01 avatar yamada-wacul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mailgun-go's Issues

Bounces

  • GET //bounces
  • GET //bounces/
  • POST //bounces
  • DELETE //bounces/

Domains

  • GET /domains
  • GET /domains/
  • POST /domains
  • DELETE /domains/
  • GET /domains//credentials
  • POST /domains//credentials
  • PUT /domains//credentials/
  • DELETE /domains//credentials/

Bounce Code Type Issue

In the Bounce struct of bounces.go, the Mailgun API returns a Code of type Int or String.

To duplicate, issue gun.GetBounces(-1, -1). If any bounces in the first page of 100 results contain a string and int in the Bounce, you'll get a type error when the json library unmarshalls the Code result to the Bounce struct.

"Error: json: cannot unmarshal string into Go value of type int"

Add support for Polling Events

Implement event polling as described in the mailgun docs here

mg := mailgun.NewMailgunFromEnv()
it := mg.PollEvents(EventOptions{ThresholdAge: time.Minute, TimeRange: time.Hour})
var events []Event
// Blocks until new events appear
for it.Poll(&events) {
	for _, event := range(events) {
		fmt.Printf("Event %+v\n", event)
	}
}

Clarify (and rename?) API keys required

The current mailgun-go requires two keys to be specified:

  • API Key
  • Public API Key

Mailgun has recently (?) changed the names of these keys, so the "Public API Key" is now called "Email Validation Key". Also, they call the "API Key" "Active API Key".

Maybe the docs should be updated to reflect this?

And it would be good if it was enough to specify only the "API Key" if validation is not used in the mailgun-go API.

Unsubscribes

  • GET //unsubscribes
  • GET //unsubscribes/
  • DELETE //unsubscribes/
  • POST //unsubscribes

Configure SMTP Server

Hi,

I'm a bit fuzzy on this, but I'm assuming the domain is the SMTP server. I basically have MailHog running, and I want all my e-mails to go to MailHog during development, but I can't find where to set the SMTP server that I hit.

I'm wondering if it's the domain we set when we create the mailgun.

Thanks.

Add travis or any other CI for tests

Hi,

do you plan to implement a CI test environment like travis, wercker, ...?
It's free and it's easier to check the contributed PRs.
You could, for example, check for test coverage, formatting and many more.

If you want, I will contribute travis or wecker integration.

Thanks and

best regards

Webhooks

  • GET /domains//webhooks
  • GET /domains//webhooks/
  • POST /domains//webhooks
  • PUT /domains//webhooks/
  • DELETE /domains//webhooks/

How does the text and HTML version work?

Exploring the Go API examples. I see that the message content is added two times in second example, first time as text (when using .newMessage()) and second time as HTML (using .setHTML()). How does this work?
Which of these contents will be sent in the email? or both? or is it something else?

Discussion: How to best report errors for the mailgun api?

@mbanzon ,

I've run into situations where a request against mailgun API would fail, but errors are not propegated back to the caller of the API, at least through the err return. I've confirmed that simplehttp is not, seemingly by design, addressing 4xx or 5xx responses as errors.

Should we be inserting an abstraction layer between mailgun-go and simplehttp that does return code validation? I'm of the opinion that this is a good idea, as it'd simplify the interface from the caller's perspective. Thoughts?

GetUnsubscribes returns unsubscribes even after RemoveUnsubscribe called

I have a background process that periodically polls for unsubscribe events. I noticed in the log that it appears to be processing the same addresses each time it runs, even though I'm calling RemoveUnsubscribe with the unsubscribe events' IDs.

Perhaps I'm doing something incorrectly, or I've misunderstood these calls.

Any help is appreciated.

use golang style iterators

Currently iterating through events looks like this.

	ei := mg.NewEventIterator()
	err := ei.GetFirstPage(GetEventsOptions{})
	for {
		if err != nil {
			return "", err
		}
		if len(ei.Events()) == 0 {
			break
		}
		for _, event := range ei.Events() {
			// Do things with **events**
		}
		err = ei.GetNext()
	}

I wish to change this to a more golang idiomatic

	it := mg.ListEvents(EventsOptions{})
	var events []Event
	for it.Next(&events) {
		for _, event := range events {
                        // Do things with **events**
		}
	}
        if it.Err() != nil {
               return "", it.Err()
        }

I bring this up because I'm currently implementing support for GET /v3/{domain}/tags which also would use an iterator as eventually we expect customers to have lots of tags. I would love to see a consistent iterator interface for the user.

Events pagination

I'm implementing a dashboard for mailgun (https://github.com/Xotelia/mailgun-dashboard). I have a question regarding the event pagination. There is a next and previous link in event API response. But with the implementation of the API I have to request the first page and then, if we are on page ten I will have to call GetNext() 10 times. Is it possible to pass an offset to GetEventsOptions? And also, how the pagination works ? based on timestamp? Offset? whatever?

Is there any way to introspect messages?

I'm trying to mock out mailgun so that all messages get captured and can later be introspected for correct application behavior.

Here's a snippet of the mock:

type MockMailGun struct {
    SentMessages chan *mailgun.Message

    // Embed the Mailgun interface. No idea what will happen if unimplemented methods are called.
    mailgun.Mailgun
}

func (mmg *MockMailGun) Send(m *mailgun.Message) (string, string, error) {
    defer func() {
        mmg.SentMessages <- m
    }()
    return "", "", nil
}

But I'm running into a problem when I try to look at the messages to introspect previous calls that were made to Message.AddHeader(), because there's no way to get the headers (or any other metadata about the message.

Here's what I'd like to be able to do:

func (mmg *MockMailGun) waitForNotification(nt NotificationType) {

    message := <-mmg.SentMessages
    if message.GetHeader("MyHeader") == "foo" {
        // Can't do this, no way to get header from message
    }

}

But there is no message.GetHeader() available in the API.

Is there any recommended way of doing this kind of mock or being able to introspect messages?

Hopefully this is the right place to ask this, I didn't see a link to a forum or group .. but let me know if I should post this somewhere else.

members.json request is not right

func (mg *MailgunImpl) CreateMemberList(s *bool, addr string, newMembers []interface{}) error {
    r := simplehttp.NewHTTPRequest(generateMemberApiUrl(listsEndpoint, addr) + ".json")
    r.SetBasicAuth(basicAuthUser, mg.ApiKey())
    p := simplehttp.NewFormDataPayload()
    if s != nil {
        p.AddValue("subscribed", yesNo(*s))
    }
    bs, err := json.Marshal(newMembers)
    if err != nil {
        return err
    }
    fmt.Println(string(bs))
    p.AddValue("members", string(bs))
    _, err = makePostRequest(r, p)
    return err
}

in the api document, no subscribed form value, you should change to same the api docuemnt. change subscribed to upsert。

p.AddValue("subscribed", yesNo(*s))
=>
p.AddValue("upsert", yesNo(s))

Unable to send mails with only HTML content

Is there a specific reason why all attributes of a message are private and there are no accessor methods? Would be really helpful to be able to read these values. I'll submit a pull request if its welcome. Wanted to know the thinking behind this decision before doing it though.

Also, it seems that a text version of the message is required. Every time I try to send a message with just an HTML body I get "Message not valid". Another conscious decision? The Mailgun API doesn't suggest that this is required and to my knowledge it shouldn't be.

Email validation occasionally causing whole application to crash

The way mailgun/mailgun-go interacts with the mbanzon/simplehttp library can sometimes result in a whole application crash (SEGFAULT) when validating email, stacktrace follows.

The way I am calling the mg.ValidateEmail function is as follows: emailvalid, _ := mg.ValidateEmail(email) // (email is a sanitised string)

This issue is paired with another issue created on the mbanzon/simplehttp repository - https://github.com/mbanzon/simplehttp/issues/8

Stacktrace:

unexpected fault address 0x7f7500000011
fatal error: fault
[signal 0xb code=0x1 addr=0x7f7500000011]

 goroutine 16 [running]:
    :0
...
:0
 goroutine 1 [IO wait]:
 main.main
    /root/gocode/src/mlc/controller.go:293 // This is just a simple log.Fatal(http.ListenAndServe("127.0.0.1:2369", m))

 goroutine 4 [chan receive]:

 goroutine 10 [select]:
 github.com_mbanzon_simplehttp.MakeRequest.pN41_github.com_mbanzon_simplehttp.HTTPRequest
    /root/gocode/src/github.com/mbanzon/simplehttp/simplehttp.go:108
 github.com_mbanzon_simplehttp.MakeGetRequest.pN41_github.com_mbanzon_simplehttp.HTTPRequest
    /root/gocode/src/github.com/mbanzon/simplehttp/simplehttp.go:59
 mailgun.getResponseFromJSON
    /root/gocode/src/github.com/mailgun/mailgun-go/rest_shim.go:72
 github.com_mailgun_mailgun_go.ValidateEmail.pN41_github.com_mailgun_mailgun_go.MailgunImpl
    /root/gocode/src/github.com/mailgun/mailgun-go/email_validation.go:49
 main.$nested0
    /root/gocode/src/mlc/controller.go:138
 github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
    /root/gocode/src/github.com/codegangsta/inject/inject.go:102
 github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:144
 github.com_go_martini_martini.Invoke.N42_github.com_go_martini_martini.routeContext
    /root/gocode/src/github.com/go-martini/martini/router.go:336
 github.com_go_martini_martini.run.pN42_github.com_go_martini_martini.routeContext
    /root/gocode/src/github.com/go-martini/martini/router.go:350
 github.com_go_martini_martini.Handle.pN35_github.com_go_martini_martini.route
    /root/gocode/src/github.com/go-martini/martini/router.go:229
 github.com_go_martini_martini.Handle.pN36_github.com_go_martini_martini.router
    /root/gocode/src/github.com/go-martini/martini/router.go:112
 github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
    /root/gocode/src/github.com/codegangsta/inject/inject.go:102
 github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:144
 github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:173
 github.com_go_martini_martini.Next.pN37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:164
 martini.$nested2
    /root/gocode/src/github.com/go-martini/martini/recovery.go:140
 github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
    /root/gocode/src/github.com/codegangsta/inject/inject.go:102
 github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:144
 github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:173
 github.com_go_martini_martini.Next.pN37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:164
 martini.$nested0
    /root/gocode/src/github.com/go-martini/martini/logger.go:25
 github.com_codegangsta_inject.Invoke.pN38_github.com_codegangsta_inject.injector
    /root/gocode/src/github.com/codegangsta/inject/inject.go:102
 github.com_go_martini_martini.Invoke.N37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:144
 github.com_go_martini_martini.run.pN37_github.com_go_martini_martini.context
    /root/gocode/src/github.com/go-martini/martini/martini.go:173
 github.com_go_martini_martini.ServeHTTP.pN37_github.com_go_martini_martini.Martini
    /root/gocode/src/github.com/go-martini/martini/martini.go:69
 github.com_go_martini_martini.ServeHTTP.N44_github.com_go_martini_martini.ClassicMartini
    /root/gocode/src/github.com/go-martini/martini/martini.go:105

Complaints

  • GET //complaints
  • GET //complaints/
  • POST //complaints
  • DELETE //complaints/

.GetStoredMessage fails with wrong URL message

// from a webhook handler
url := r.PostFormValue("message-url")
urlp := strings.Split(url, "/")
err := mailgun.Client.GetStoredMessage(urlp[len(urlp)-1])

Generates an error that reads as

UnexpectedResponseError
URL=https://api.mailgun.net/v2/domains/mydomain.com/messages/eyJwIjpmYWxzZSaWFwiayZDgxNzc0LWJaWFlMzItNGYS1hM2E2LTc4NzUwYWRhZjMaWF3OCIsInMiOiJkYzRjZmIiwiYyI6InNhaWFkIn0=
ExpectedOneOf=[]int{200, 202, 204}
Got=400
Error: {"message":"Please use URLs as they are given in events/webhooks"}

While the value of url was https://si.api.mailgun.net/v3/domains/mydomain.com/messages/eyJwIjpmYWxzZSaWFwiayZDgxNzc0LWJaWFlMzItNGYS1hM2E2LTc4NzUwYWRhZjMaWF3OCIsInMiOiJkYzRjZmIiwiYyI6InNhaWFkIn0=.

Mailing Lists

  • GET /lists
  • GET /lists/
  • POST /lists
  • PUT /lists/
  • DELETE /lists/
  • GET /lists//members
  • GET /lists//members/<member_address>
  • POST /lists//members
  • PUT /lists//members/<member_address>
  • POST /lists//members.json
  • DELETE /lists//members/<member_address>

Remove simplehttp dependency

This heavily relates to #24.

IMHO dependencies to external libraries should be removed if possible. The only reason that mailgun-go use simplehttp is that I originally wrote both libraries in parallel.

Hiding simplehttp and its implementation bring trouble when it is needed to use alternative http.Client or http.Transport (when writing application for GAE eg.).

Campaigns

  • GET //campaigns
  • GET //campaigns/
  • POST //campaigns
  • PUT //campaigns/
  • DELETE //campaigns/
  • GET //campaigns//events
  • GET //campaigns//stats
  • GET //campaigns//clicks
  • GET //campaigns//opens
  • GET //campaigns//unsubscribes
  • GET //campaigns//complaints

Webhooks Timeout

The functions to create and update webhooks time out frequently.

too many open files error?

Already adjusted to

ulimit -n
65536

but still get lots of error:

Get https://api.mailgun.net/v2/address/parse?addresses=foo%40bar.com: dial tcp: lookup api.mailgun.net on 10.0.0.2:53: dial udp 10.0.0.2:53: too many open files

my codes:

func send(subject, body string, to ...string) error {
    mg := mailgun.NewMailgun(mailgunDomain, mailgunPrivateKey, mailgunPublicKey)
    addrs, err := parseAddrs(mg, to...)
    if err != nil {
        return err
    }

    m := mg.NewMessage(
        mailgunFrom, // From
        subject,     // Subject
        body,        // Plain-text body
        addrs...,    // Recipients (vararg list)
    )

    _, _, err = mg.Send(m)
    return err
}

func parseAddrs(mg mailgun.Mailgun, to ...string) ([]string, error) {
    addrs, unparseable, err := mg.ParseAddresses(to...)
    if err != nil {
        return nil, err
    }

    for _, invalid := range unparseable {
        fmt.Println(invalid)
        // todo send slack alert
    }

    return addrs, nil
}

API v3

Is the upgrade to API v3 planned? Or is this project abandoned?

I'm not sure if there are any changes, but the URL changed, so there should be a reason for that? (at least we should update the URL mailgun-go uses.

Routes

  • GET /routes
  • GET /routes/
  • POST /routes
  • PUT /routes/
  • DELETE /routes/

package "testing" imported in non-testing code

In acceptance.go, package "testing" is imported, and so is imported by anything using the mailgun go package. (In particular, that showed up to me as the testing command line flags showing up in a main package that was using the mailgun package.)

Perhaps acceptance.go should instead be acceptance_test.go? It seems the functions within are only used by other *_test.go files.

Stats

  • GET //stats
  • DELETE //tags/

Messages

  • POST //messages
  • POST //messages.mime
  • GET domains//messages
  • DELETE domains//messages/

Compilation error on "acceptance"

The acceptance package can't be compiled without the proper build tag.

Some files are missing the build tag but requiring the function reqEnv (included in a file that has the build tag) making a standard build of the package fail.

NewMailgun - publicApiKey

I suppose the function

func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
    m := MailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey}
    return &m
}

has a publicApiKey because the API v2 uses it? I cannot find any reference to it at the documentation / website of Mailgun, and leaving it blank enables me to send the e-mail anyway.

What's its use? And why is it still there?

GetStoredMessage fails with "Please use URLs as they are given in events/webhooks"

When passing in the storage.key of an event to the GetStoredMessage function, I get something like:

UnexpectedResponseError
URL=https://api.mailgun.net/v3/domains/example.com/messages/STORAGEKEY
ExpectedOneOf=[]int{200, 202, 204} Got=400
Error: {"message":"Please use URLs as they are given in events/webhooks"}

The URL provided by the event's storage.url uses a slightly different domain:

https://so.api.mailgun.net/v3/domains/example.com/messages/STORAGEKEY

Which, if followed (with proper credentials) gives the proper response.

The only difference from the URL generated by GetStoredMessage is the "so." sub-sub-domain, for what that is worth.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.