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 }