dnscontrol/vendor/github.com/tiramiseb/go-gandi/internal/client/gandi.go
Tom Limoncelli f708f19535
Update many modules (#620)
* Update many modules.

go get -u golang.org/x/net
go get -u github.com/aws/aws-sdk-go
go get -u golang.org/x/oauth2
go get -u google.golang.org/api
go get -u gopkg.in/yaml.v2
go mod tidy

* Update modules

go get -u github.com/tiramiseb/go-gandi
go get -u github.com/miekg/dns
go mod tidy
2020-02-12 21:16:35 -05:00

174 lines
5.5 KiB
Go

package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"strings"
)
const (
gandiEndpoint = "https://api.gandi.net/v5/"
)
// Gandi is the handle used to interact with the Gandi API
type Gandi struct {
apikey string
endpoint string
sharingID string
debug bool
dryRun bool
}
// New instantiates a new Gandi client
func New(apikey string, sharingID string, debug bool, dryRun bool) *Gandi {
return &Gandi{apikey: apikey, endpoint: gandiEndpoint, sharingID: sharingID, debug: debug, dryRun: dryRun}
}
// SetEndpoint sets the URL to the endpoint. It takes a string defining the subpath under https://api.gandi.net/v5/
func (g *Gandi) SetEndpoint(endpoint string) {
g.endpoint = gandiEndpoint + endpoint
}
// Get issues a GET request. It takes a subpath rooted in the endpoint. Response data is written to the recipient.
// Returns the response headers and any error
func (g *Gandi) Get(path string, params, recipient interface{}) (http.Header, error) {
return g.askGandi(http.MethodGet, path, params, recipient)
}
// Post issues a POST request. It takes a subpath rooted in the endpoint. Response data is written to the recipient.
// Returns the response headers and any error
func (g *Gandi) Post(path string, params, recipient interface{}) (http.Header, error) {
return g.askGandi(http.MethodPost, path, params, recipient)
}
// Patch issues a PATCH request. It takes a subpath rooted in the endpoint. Response data is written to the recipient.
// Returns the response headers and any error
func (g *Gandi) Patch(path string, params, recipient interface{}) (http.Header, error) {
return g.askGandi(http.MethodPatch, path, params, recipient)
}
// Delete issues a DELETE request. It takes a subpath rooted in the endpoint. Response data is written to the recipient.
// Returns the response headers and any error
func (g *Gandi) Delete(path string, params, recipient interface{}) (http.Header, error) {
return g.askGandi(http.MethodDelete, path, params, recipient)
}
// Put issues a PUT request. It takes a subpath rooted in the endpoint. Response data is written to the recipient.
// Returns the response headers and any error
func (g *Gandi) Put(path string, params, recipient interface{}) (http.Header, error) {
return g.askGandi(http.MethodPut, path, params, recipient)
}
func (g *Gandi) askGandi(method, path string, params, recipient interface{}) (http.Header, error) {
resp, err := g.doAskGandi(method, path, params, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
decoder.Decode(recipient)
return resp.Header, nil
}
// GetBytes issues a GET request but does not attempt to parse any response into JSON.
// It returns the response headers, a byteslice of the response, and any error
func (g *Gandi) GetBytes(path string, params interface{}) (http.Header, []byte, error) {
headers := [][2]string{
{"Accept", "text/plain"},
}
resp, err := g.doAskGandi(http.MethodGet, path, params, headers)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
return resp.Header, content, err
}
func (g *Gandi) doAskGandi(method, path string, p interface{}, extraHeaders [][2]string) (*http.Response, error) {
var (
err error
req *http.Request
)
params, err := json.Marshal(p)
if err != nil {
return nil, err
}
client := &http.Client{}
suffix := ""
if len(g.sharingID) != 0 {
suffix += "?sharing_id=" + g.sharingID
}
if params != nil && string(params) != "null" {
req, err = http.NewRequest(method, g.endpoint+path+suffix, bytes.NewReader(params))
} else {
req, err = http.NewRequest(method, g.endpoint+path+suffix, nil)
}
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Apikey "+g.apikey)
req.Header.Add("Content-Type", "application/json")
if g.dryRun {
req.Header.Add("Dry-Run", "1")
}
for _, header := range extraHeaders {
req.Header.Add(header[0], header[1])
}
if g.debug {
dump, _ := httputil.DumpRequestOut(req, true)
fmt.Println("=======================================\nREQUEST:")
fmt.Println(string(dump))
}
resp, err := client.Do(req)
if err != nil {
return resp, err
}
if g.debug {
dump, _ := httputil.DumpResponse(resp, true)
fmt.Println("=======================================\nREQUEST:")
fmt.Println(string(dump))
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
defer resp.Body.Close()
var message StandardResponse
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
decoder.Decode(&message)
if message.Message != "" {
err = fmt.Errorf("%d: %s", resp.StatusCode, message.Message)
} else if len(message.Errors) > 0 {
var errors []string
for _, oneError := range message.Errors {
errors = append(errors, fmt.Sprintf("%s: %s", oneError.Name, oneError.Description))
}
err = fmt.Errorf(strings.Join(errors, ", "))
} else {
err = fmt.Errorf("%d", resp.StatusCode)
}
}
return resp, err
}
// StandardResponse is a standard response
type StandardResponse struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
UUID string `json:"uuid,omitempty"`
Object string `json:"object,omitempty"`
Cause string `json:"cause,omitempty"`
Status string `json:"status,omitempty"`
Errors []StandardError `json:"errors,omitempty"`
}
// StandardError is embedded in a standard error
type StandardError struct {
Location string `json:"location"`
Name string `json:"name"`
Description string `json:"description"`
}