diff --git a/vendor/github.com/prasmussen/gandi-api/client/client.go b/vendor/github.com/prasmussen/gandi-api/client/client.go index 225fab81e..65f13d052 100644 --- a/vendor/github.com/prasmussen/gandi-api/client/client.go +++ b/vendor/github.com/prasmussen/gandi-api/client/client.go @@ -1,26 +1,50 @@ package client -import "github.com/kolo/xmlrpc" +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" -const ( - Production SystemType = iota - Testing + "github.com/kolo/xmlrpc" ) +const ( + // Production is the SystemType to provide to New to use the production XML API + Production SystemType = iota + // Testing is the SystemType to provide to New to use the test XML API + Testing + // LiveDNS is the SystemType to provide to New to use the Live DNS REST API + // Full documentation of the API is available here: http://doc.livedns.gandi.net/ + LiveDNS +) + +// SystemType is the type used to resolve gandi API address type SystemType int -func (self SystemType) Url() string { - if self == Production { +// Url returns the actual gandi API base URL +func (s SystemType) Url() string { + if s == Production { return "https://rpc.gandi.net/xmlrpc/" } + if s == LiveDNS { + return "https://dns.api.gandi.net/api/v5/" + } return "https://rpc.ote.gandi.net/xmlrpc/" } +// Client holds the configuration of a gandi client type Client struct { + // Key is the API key to provide to gandi Key string + // Url is the base URL of the gandi API Url string } +// New creates a new gandi client for the given system func New(apiKey string, system SystemType) *Client { return &Client{ Key: apiKey, @@ -28,10 +52,145 @@ func New(apiKey string, system SystemType) *Client { } } -func (self *Client) Call(serviceMethod string, args []interface{}, reply interface{}) error { - rpc, err := xmlrpc.NewClient(self.Url, nil) +// Call performs an acual XML RPC call to the gandi API +func (c *Client) Call(serviceMethod string, args []interface{}, reply interface{}) error { + rpc, err := xmlrpc.NewClient(c.Url, nil) if err != nil { return err } return rpc.Call(serviceMethod, args, reply) } + +// DoRest performs a request to gandi LiveDNS api and optionnally decodes the reply +func (c *Client) DoRest(req *http.Request, decoded interface{}) (*http.Response, error) { + if decoded != nil { + req.Header.Set("Accept", "application/json") + } + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode == http.StatusUnauthorized { + return nil, fmt.Errorf("the server returned unauthorized code. Your API key might be invalid or have expired") + } + // + defer func() { err = resp.Body.Close() }() + if decoded != nil { + b, e := ioutil.ReadAll(resp.Body) + if e != nil { + return nil, e + } + if len(b) > 0 { + e = json.Unmarshal(b, decoded) + if e != nil { + return nil, e + } + } + resp.Body = ioutil.NopCloser(bytes.NewReader(b)) + } + return resp, err +} + +// NewJSONRequest creates a new authenticated to gandi live DNS REST API. +// If data is not null, it will be encoded as json and prodived in the request body +func (c *Client) NewJSONRequest(method string, url string, data interface{}) (*http.Request, error) { + var reader io.Reader + if data != nil { + b, err := json.Marshal(data) + if err != nil { + return nil, err + } + reader = bytes.NewReader(b) + } + req, err := http.NewRequest(method, fmt.Sprintf("%s/%s", strings.TrimRight(c.Url, "/"), strings.TrimLeft(url, "/")), reader) + if err != nil { + return nil, err + } + if data != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("X-Api-Key", c.Key) + return req, nil +} + +// Get performs a Get request to gandi Live DNS api and decodes the returned data if a not null decoded pointer is provided +func (c *Client) Get(URI string, decoded interface{}) (*http.Response, error) { + req, err := c.NewJSONRequest("GET", URI, nil) + if err != nil { + return nil, err + } + resp, err := c.DoRest(req, decoded) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusOK) + } + return resp, err +} + +// Delete performs a Delete request to gandi Live DNS api and decodes the returned data if a not null decoded pointer is provided +func (c *Client) Delete(URI string, decoded interface{}) (*http.Response, error) { + req, err := c.NewJSONRequest("DELETE", URI, nil) + if err != nil { + return nil, err + } + resp, err := c.DoRest(req, decoded) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusNoContent { + return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusNoContent) + } + return resp, err +} + +// Post performs a Post request request to gandi Live DNS api +// - with data encoded as JSON if a not null data pointer is provided +// - decodes the returned data if a not null decoded pointer is provided +// - ensures the status code is an HTTP accepted +func (c *Client) Post(URI string, data interface{}, decoded interface{}) (*http.Response, error) { + req, err := c.NewJSONRequest("POST", URI, data) + if err != nil { + return nil, err + } + resp, err := c.DoRest(req, decoded) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusCreated) + } + return resp, err +} + +// Put performs a Put request to gandi Live DNS api +// - with data encoded as JSON if a not null data pointer is provided +// - decodes the returned data if a not null decoded pointer is provided +func (c *Client) Put(URI string, data interface{}, decoded interface{}) (*http.Response, error) { + req, err := c.NewJSONRequest("PUT", URI, data) + if err != nil { + return nil, err + } + return c.DoRest(req, decoded) +} + +// Patch performs a Patch request to gandi Live DNS api +// - with data encoded as JSON if a not null data pointer is provided +// - decodes the returned data if a not null decoded pointer is provided +// - ensures the status code is an HTTP accepted +func (c *Client) Patch(URI string, data interface{}, decoded interface{}) (*http.Response, error) { + req, err := c.NewJSONRequest("PATCH", URI, data) + if err != nil { + return nil, err + } + resp, err := c.DoRest(req, decoded) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusAccepted { + return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusAccepted) + } + return resp, err +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 93223064f..66070b0f7 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -339,52 +339,52 @@ "revisionTime": "2017-09-08T20:15:21Z" }, { - "checksumSHA1": "nS4kKHjMlJpQg3sixqelpCg1jyk=", + "checksumSHA1": "iR+qtc2CtY1XRrdavOMZ6R1eJv0=", "path": "github.com/prasmussen/gandi-api/client", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "US0LGupyHvXgr2iDH7kw4f7KOcc=", "path": "github.com/prasmussen/gandi-api/domain", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "QCTGEMnU3WqIX51TqbRvAuwu4Wo=", "path": "github.com/prasmussen/gandi-api/domain/nameservers", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "/t4nBKtJF6W3PHzTBNAzgcw54GU=", "path": "github.com/prasmussen/gandi-api/domain/zone", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "wmLJwLc3SlIGhio+vWofJq4XSTU=", "path": "github.com/prasmussen/gandi-api/domain/zone/record", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "avzpVkEeXzDiaNLsl4agXWm9tm0=", "path": "github.com/prasmussen/gandi-api/domain/zone/version", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "txVNPkzE0Jkag20VZ1hLj/Td+O4=", "path": "github.com/prasmussen/gandi-api/operation", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "FwIh1Rk/XNANaxCz3Fw/vQ3Hu5E=", "path": "github.com/prasmussen/gandi-api/util", - "revision": "a23dc46a847ad0f56c9d66cd84ffeeb002f2c6d3", - "revisionTime": "2017-11-24T17:13:09Z" + "revision": "00637462a528a482a9f3486333be02266bce2086", + "revisionTime": "2018-01-16T21:20:20Z" }, { "checksumSHA1": "NLTyYVX4dn8jV1iad7p494AiZ8E=",