rclone/vendor/github.com/pengsrc/go-shared/rest/rest.go

152 lines
3.5 KiB
Go

package rest
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/Jeffail/gabs"
)
// Method contains the supported HTTP verbs.
type Method string
// Supported HTTP verbs.
const (
Get Method = "GET"
Post Method = "POST"
Put Method = "PUT"
Patch Method = "PATCH"
Delete Method = "DELETE"
)
// Request holds the request to an API Call.
type Request struct {
Method Method
BaseURL string // e.g. https://api.service.com
Headers map[string]string
QueryParams map[string]string
Body []byte
}
// Response holds the response from an API call.
type Response struct {
StatusCode int // e.g. 200
Headers http.Header // e.g. map[X-Rate-Limit:[600]]
Body string // e.g. {"result: success"}
JSON *gabs.Container
}
// ParseJSON parses the response body to JSON container.
func (r *Response) ParseJSON() error {
if strings.Contains(r.Headers.Get("Content-Type"), "application/json") {
json, err := gabs.ParseJSON([]byte(r.Body))
if err != nil {
return err
}
r.JSON = json
return nil
}
return errors.New("response body is not JSON")
}
// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = &Client{HTTPClient: http.DefaultClient}
// Client allows modification of client headers, redirect policy
// and other settings
// See https://golang.org/pkg/net/http
type Client struct {
HTTPClient *http.Client
}
// The following functions enable the ability to define a
// custom HTTP Client
// MakeRequest makes the API call.
func (c *Client) MakeRequest(req *http.Request) (*http.Response, error) {
return c.HTTPClient.Do(req)
}
// API is the main interface to the API.
func (c *Client) API(r *Request) (*Response, error) {
// Build the HTTP request object.
req, err := BuildRequestObject(r)
if err != nil {
return nil, err
}
// Build the HTTP client and make the request.
res, err := c.MakeRequest(req)
if err != nil {
return nil, err
}
// Build Response object.
response, err := BuildResponse(res)
if err != nil {
return nil, err
}
return response, nil
}
// AddQueryParameters adds query parameters to the URL.
func AddQueryParameters(baseURL string, queryParams map[string]string) string {
baseURL += "?"
params := url.Values{}
for key, value := range queryParams {
params.Add(key, value)
}
return baseURL + params.Encode()
}
// BuildRequestObject creates the HTTP request object.
func BuildRequestObject(r *Request) (*http.Request, error) {
// Add any query parameters to the URL.
if len(r.QueryParams) != 0 {
r.BaseURL = AddQueryParameters(r.BaseURL, r.QueryParams)
}
req, err := http.NewRequest(string(r.Method), r.BaseURL, bytes.NewBuffer(r.Body))
for key, value := range r.Headers {
req.Header.Set(key, value)
}
_, exists := req.Header["Content-Type"]
if len(r.Body) > 0 && !exists {
req.Header.Set("Content-Type", "application/json")
}
return req, err
}
// BuildResponse builds the response struct.
func BuildResponse(r *http.Response) (*Response, error) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
defer r.Body.Close()
response := Response{
StatusCode: r.StatusCode,
Body: string(body),
Headers: r.Header,
}
return &response, nil
}
// MakeRequest makes the API call.
func MakeRequest(r *http.Request) (*http.Response, error) {
return DefaultClient.HTTPClient.Do(r)
}
// API is the main interface to the API.
func API(request *Request) (*Response, error) {
return DefaultClient.API(request)
}