terraform/vendor/github.com/arukasio/cli/client.go

325 lines
7.5 KiB
Go

package arukas
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/manyminds/api2go/jsonapi"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"reflect"
"sort"
"strings"
"time"
)
// VERSION is cli version.
const VERSION = "v0.1.3"
// Client represents a user client data in struct variables.
type Client struct {
APIURL *url.URL
HTTP *http.Client
Username string
Password string
UserAgent string
Debug bool
Output func(...interface{})
OutputDest io.Writer
Timeout time.Duration
}
var (
client *Client
)
// PrintHeaderln print as the values.
func (c *Client) PrintHeaderln(values ...interface{}) {
fmt.Fprint(c.OutputDest, ToTSV(values[1:]), "\n")
}
// Println print as the values.
func (c *Client) Println(values ...interface{}) {
fmt.Fprint(c.OutputDest, ToTSV(values[1:]), "\n")
}
// Get return *c as the get path of API request.
func (c *Client) Get(v interface{}, path string) error {
return c.APIReq(v, "GET", path, nil)
}
// Patch return *c as the patch path of API request.
func (c *Client) Patch(v interface{}, path string, body interface{}) error {
return c.APIReq(v, "PATCH", path, body)
}
// Post return *c as the post path of API request.
func (c *Client) Post(v interface{}, path string, body interface{}) error {
return c.APIReq(v, "POST", path, body)
}
// Put return *c as the put path of API request.
func (c *Client) Put(v interface{}, path string, body interface{}) error {
return c.APIReq(v, "PUT", path, body)
}
// Delete return *c as the delete path of API request.
func (c *Client) Delete(path string) error {
return c.APIReq(nil, "DELETE", path, nil)
}
// NewClientWithOsExitOnErr return client.
func NewClientWithOsExitOnErr() *Client {
client, err := NewClient()
if err != nil {
log.Fatal(err)
}
return client
}
// NewClient returns a new arukas client, requires an authorization key.
// You can generate a API key by visiting the Keys section of the Arukas
// control panel for your account.
func NewClient() (*Client, error) {
debug := false
if os.Getenv("ARUKAS_DEBUG") != "" {
debug = true
}
apiURL := "https://app.arukas.io/api/"
if os.Getenv("ARUKAS_JSON_API_URL") != "" {
apiURL = os.Getenv("ARUKAS_JSON_API_URL")
}
client := new(Client)
parsedURL, err := url.Parse(apiURL)
if err != nil {
fmt.Println("url err")
return nil, err
}
parsedURL.Path = strings.TrimRight(parsedURL.Path, "/")
client.APIURL = parsedURL
client.UserAgent = "Arukas CLI (" + VERSION + ")"
client.Debug = debug
client.OutputDest = os.Stdout
client.Timeout = 30 * time.Second
if username := os.Getenv("ARUKAS_JSON_API_TOKEN"); username != "" {
client.Username = username
} else {
return nil, errors.New("ARUKAS_JSON_API_TOKEN is not set")
}
if password := os.Getenv("ARUKAS_JSON_API_SECRET"); password != "" {
client.Password = password
} else {
return nil, errors.New("ARUKAS_JSON_API_SECRET is not set")
}
return client, nil
}
// NewRequest Generates an HTTP request for the Arukas API, but does not
// perform the request. The request's Accept header field will be
// set to:
//
// Accept: application/vnd.api+json;
//
// The type of body determines how to encode the request:
//
// nil no body
// io.Reader body is sent verbatim
// []byte body is encoded as application/vnd.api+json
// else body is encoded as application/json
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {
var ctype string
var rbody io.Reader
switch t := body.(type) {
case nil:
case string:
rbody = bytes.NewBufferString(t)
case io.Reader:
rbody = t
case []byte:
rbody = bytes.NewReader(t)
ctype = "application/vnd.api+json"
default:
v := reflect.ValueOf(body)
if !v.IsValid() {
break
}
if v.Type().Kind() == reflect.Ptr {
v = reflect.Indirect(v)
if !v.IsValid() {
break
}
}
j, err := json.Marshal(body)
if err != nil {
return nil, err
}
rbody = bytes.NewReader(j)
ctype = "application/json"
}
requestURL := *c.APIURL // shallow copy
requestURL.Path += path
if c.Debug {
fmt.Printf("Requesting: %s %s %s\n", method, requestURL, rbody)
}
req, err := http.NewRequest(method, requestURL.String(), rbody)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/vnd.api+json")
req.Header.Set("User-Agent", c.UserAgent)
if ctype != "" {
req.Header.Set("Content-Type", ctype)
}
req.SetBasicAuth(c.Username, c.Password)
return req, nil
}
// APIReq Sends a Arukas API request and decodes the response into v.
// As described in NewRequest(), the type of body determines how to
// encode the request body. As described in DoReq(), the type of
// v determines how to handle the response body.
func (c *Client) APIReq(v interface{}, method, path string, body interface{}) error {
var marshaled []byte
var err1 error
var req *http.Request
if body != nil {
var err error
_, ok := body.(jsonapi.MarshalIdentifier)
if ok {
marshaled, err = jsonapi.Marshal(body)
} else {
marshaled, err = json.Marshal(body)
}
if err != nil {
return err
}
if c.Debug {
fmt.Println("json: ", string(marshaled))
}
req, err1 = c.NewRequest(method, path, marshaled)
} else {
req, err1 = c.NewRequest(method, path, body)
}
if err1 != nil {
return err1
}
return c.DoReq(req, v)
}
// DoReq Submits an HTTP request, checks its response, and deserializes
// the response into v. The type of v determines how to handle
// the response body:
//
// nil body is discarded
// io.Writer body is copied directly into v
// else body is decoded into v as json
//
func (c *Client) DoReq(req *http.Request, v interface{}) error {
httpClient := c.HTTP
if httpClient == nil {
httpClient = &http.Client{
Timeout: c.Timeout,
}
}
res, err := httpClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if c.Debug {
fmt.Println("Status:", res.StatusCode)
headers := make([]string, len(res.Header))
for k := range res.Header {
headers = append(headers, k)
}
sort.Strings(headers)
for _, k := range headers {
if k != "" {
fmt.Println(k+":", strings.Join(res.Header[k], " "))
}
}
fmt.Println(string(body))
}
if err = checkResponse(res); err != nil {
return err
}
switch t := v.(type) {
case nil:
case io.Writer:
_, err = io.Copy(t, res.Body)
default:
err = jsonapi.Unmarshal(body, v)
if err != nil {
err = json.Unmarshal(body, v)
}
}
return err
}
// CheckResponse returns an error (of type *Error) if the response.
func checkResponse(res *http.Response) error {
if res.StatusCode == 404 {
return fmt.Errorf("The resource does not found on the server: %s", res.Request.URL)
} else if res.StatusCode >= 400 {
return fmt.Errorf("Got HTTP status code >= 400: %s", res.Status)
}
return nil
}
// PrintTsvln print Tab-separated values line.
func PrintTsvln(values ...interface{}) {
fmt.Println(ToTSV(values))
}
// ToTSV return Tab-separated values.
func ToTSV(values []interface{}) string {
var str []string
for _, s := range values {
if v, ok := s.(string); ok {
str = append(str, string(v))
} else {
str = append(str, fmt.Sprint(s))
}
}
return strings.Join(str, "\t")
}
// SplitTSV return splited Tab-separated values.
func SplitTSV(str string) []string {
splitStr := strings.Split(str, "\t")
var trimmed []string
for _, v := range splitStr {
trimmed = append(trimmed, strings.Trim(v, "\n"))
}
return trimmed
}
// removeFirstLine is remove first line.
func removeFirstLine(str string) string {
lines := strings.Split(str, "\n")
return strings.Join(lines[1:], "\n")
}