2017-02-13 05:35:14 -05:00
|
|
|
package bmcs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"github.com/google/go-querystring/query"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
contentType = "Content-Type"
|
|
|
|
jsonContentType = "application/json"
|
|
|
|
)
|
|
|
|
|
|
|
|
// baseClient provides a basic (AND INTENTIONALLY INCOMPLETE) JSON REST client
|
|
|
|
// that abstracts away some of the repetitive code required in the BMCS Client.
|
|
|
|
type baseClient struct {
|
|
|
|
httpClient *http.Client
|
|
|
|
method string
|
|
|
|
url string
|
|
|
|
queryStruct interface{}
|
|
|
|
header http.Header
|
|
|
|
body interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// newBaseClient constructs a default baseClient.
|
|
|
|
func newBaseClient() *baseClient {
|
|
|
|
return &baseClient{
|
|
|
|
httpClient: http.DefaultClient,
|
|
|
|
method: "GET",
|
|
|
|
header: make(http.Header),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a copy of an existing baseClient.
|
|
|
|
func (c *baseClient) New() *baseClient {
|
|
|
|
// Copy headers
|
|
|
|
header := make(http.Header)
|
|
|
|
for k, v := range c.header {
|
|
|
|
header[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return &baseClient{
|
|
|
|
httpClient: c.httpClient,
|
|
|
|
method: c.method,
|
|
|
|
url: c.url,
|
|
|
|
header: header,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Client sets the http Client used to perform requests.
|
|
|
|
func (c *baseClient) Client(httpClient *http.Client) *baseClient {
|
|
|
|
if httpClient == nil {
|
|
|
|
c.httpClient = http.DefaultClient
|
|
|
|
} else {
|
|
|
|
c.httpClient = httpClient
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// Base sets the base client url.
|
|
|
|
func (c *baseClient) Base(path string) *baseClient {
|
|
|
|
c.url = path
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// Path extends the client url.
|
|
|
|
func (c *baseClient) Path(path string) *baseClient {
|
|
|
|
baseURL, baseErr := url.Parse(c.url)
|
|
|
|
pathURL, pathErr := url.Parse(path)
|
|
|
|
// Bail on parsing error leaving the client's url unmodified
|
|
|
|
if baseErr != nil || pathErr != nil {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
c.url = baseURL.ResolveReference(pathURL).String()
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// QueryStruct sets the struct from which the request querystring is built.
|
|
|
|
func (c *baseClient) QueryStruct(params interface{}) *baseClient {
|
|
|
|
c.queryStruct = params
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetBody wraps a given struct for serialisation and sets the client body.
|
|
|
|
func (c *baseClient) SetBody(params interface{}) *baseClient {
|
|
|
|
c.body = params
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
|
|
|
// AddHeader adds a HTTP header to the client. Existing keys will be extended.
|
|
|
|
func (c *baseClient) AddHeader(key, value string) *baseClient {
|
|
|
|
c.header.Add(key, value)
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetHeader sets a HTTP header on the client. Existing keys will be
|
|
|
|
// overwritten.
|
|
|
|
func (c *baseClient) SetHeader(key, value string) *baseClient {
|
|
|
|
c.header.Add(key, value)
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTP methods (subset)
|
|
|
|
|
|
|
|
// Get sets the client's HTTP method to GET.
|
|
|
|
func (c *baseClient) Get(path string) *baseClient {
|
|
|
|
c.method = "GET"
|
|
|
|
return c.Path(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Post sets the client's HTTP method to POST.
|
|
|
|
func (c *baseClient) Post(path string) *baseClient {
|
|
|
|
c.method = "POST"
|
|
|
|
return c.Path(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete sets the client's HTTP method to DELETE.
|
|
|
|
func (c *baseClient) Delete(path string) *baseClient {
|
|
|
|
c.method = "DELETE"
|
|
|
|
return c.Path(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do executes a HTTP request and returns the response encoded as either error
|
|
|
|
// or success values.
|
|
|
|
func (c *baseClient) Do(req *http.Request, successV, failureV interface{}) (*http.Response, error) {
|
|
|
|
resp, err := c.httpClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
|
|
if successV != nil {
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(successV)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if failureV != nil {
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(failureV)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request builds a http.Request from the baseClient instance.
|
|
|
|
func (c *baseClient) Request() (*http.Request, error) {
|
|
|
|
reqURL, err := url.Parse(c.url)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-14 04:50:28 -05:00
|
|
|
if c.queryStruct != nil {
|
|
|
|
err = addQueryStruct(reqURL, c.queryStruct)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-13 05:35:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
body := &bytes.Buffer{}
|
|
|
|
if c.body != nil {
|
|
|
|
if err := json.NewEncoder(body).Encode(c.body); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest(c.method, reqURL.String(), body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add headers to request
|
|
|
|
for k, vs := range c.header {
|
|
|
|
for _, v := range vs {
|
|
|
|
req.Header.Add(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recieve creates a http request from the client and executes it returning the
|
|
|
|
// response.
|
|
|
|
func (c *baseClient) Receive(successV, failureV interface{}) (*http.Response, error) {
|
|
|
|
req, err := c.Request()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return c.Do(req, successV, failureV)
|
|
|
|
}
|
|
|
|
|
|
|
|
// addQueryStruct converts a struct to a querystring and merges any values
|
|
|
|
// provided in the URL itself.
|
|
|
|
func addQueryStruct(reqURL *url.URL, queryStruct interface{}) error {
|
|
|
|
urlValues, err := url.ParseQuery(reqURL.RawQuery)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
queryValues, err := query.Values(queryStruct)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, vs := range queryValues {
|
|
|
|
for _, v := range vs {
|
|
|
|
urlValues.Add(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reqURL.RawQuery = urlValues.Encode()
|
|
|
|
return nil
|
|
|
|
}
|