terraform/vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go

289 lines
7.4 KiB
Go
Raw Normal View History

Ns1 provider (#10782) * vendor: update gopkg.in/ns1/ns1-go.v2 * provider/ns1: Port the ns1 provider to Terraform core * docs/ns1: Document the ns1 provider * ns1: rename remaining nsone -> ns1 (#10805) * Ns1 provider (#11300) * provider/ns1: Flesh out support for meta structs. Following the structure outlined by @pashap. Using reflection to reduce copy/paste. Putting metas inside single-item lists. This is clunky, but I couldn't figure out how else to have a nested struct. Maybe the Terraform people know a better way? Inside the meta struct, all fields are always written to the state; I can't figure out how to omit fields that aren't used. This is not just verbose, it actually causes issues because you can't have both "up" and "up_feed" set). Also some minor other changes: - Add "terraform" import support to records and zones. - Create helper class StringEnum. * provider/ns1: Make fmt * provider/ns1: Remove stubbed out RecordRead (used for testing metadata change). * provider/ns1: Need to get interface that m contains from Ptr Value with Elem() * provider/ns1: Use empty string to indicate no feed given. * provider/ns1: Remove old record.regions fields. * provider/ns1: Removes redundant testAccCheckRecordState * provider/ns1: Moves account permissions logic to permissions.go * provider/ns1: Adds tests for team resource. * provider/ns1: Move remaining permissions logic to permissions.go * ns1/provider: Adds datasource.config * provider/ns1: Small clean up of datafeed resource tests * provider/ns1: removes testAccCheckZoneState in favor of explicit name check * provider/ns1: More renaming of nsone -> ns1 * provider/ns1: Comment out metadata for the moment. * Ns1 provider (#11347) * Fix the removal of empty containers from a flatmap Removal of empty nested containers from a flatmap would sometimes fail a sanity check when removed in the wrong order. This would only fail sometimes due to map iteration. There was also an off-by-one error in the prefix check which could match the incorrect keys. * provider/ns1: Adds ns1 go client through govendor. * provider/ns1: Removes unused debug line * docs/ns1: Adds docs around apikey/datasource/datafeed/team/user/record. * provider/ns1: Gets go vet green
2017-01-23 22:41:07 +01:00
package rest
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
)
const (
clientVersion = "2.0.0"
defaultEndpoint = "https://api.nsone.net/v1/"
defaultUserAgent = "go-ns1/" + clientVersion
headerAuth = "X-NSONE-Key"
headerRateLimit = "X-Ratelimit-Limit"
headerRateRemaining = "X-Ratelimit-Remaining"
headerRatePeriod = "X-Ratelimit-Period"
)
// Doer is a single method interface that allows a user to extend/augment an http.Client instance.
// Note: http.Client satisfies the Doer interface.
type Doer interface {
Do(*http.Request) (*http.Response, error)
}
// Client manages communication with the NS1 Rest API.
type Client struct {
// httpClient handles all rest api communication,
// and expects an *http.Client.
httpClient Doer
// NS1 rest endpoint, overrides default if given.
Endpoint *url.URL
// NS1 api key (value for http request header 'X-NSONE-Key').
APIKey string
// NS1 go rest user agent (value for http request header 'User-Agent').
UserAgent string
// Func to call after response is returned in Do
RateLimitFunc func(RateLimit)
// From the excellent github-go client.
common service // Reuse a single struct instead of allocating one for each service on the heap.
// Services used for communicating with different components of the NS1 API.
APIKeys *APIKeysService
DataFeeds *DataFeedsService
DataSources *DataSourcesService
Jobs *JobsService
Notifications *NotificationsService
Records *RecordsService
Settings *SettingsService
Teams *TeamsService
Users *UsersService
Warnings *WarningsService
Zones *ZonesService
}
// NewClient constructs and returns a reference to an instantiated Client.
func NewClient(httpClient Doer, options ...func(*Client)) *Client {
endpoint, _ := url.Parse(defaultEndpoint)
if httpClient == nil {
httpClient = http.DefaultClient
}
c := &Client{
httpClient: httpClient,
Endpoint: endpoint,
RateLimitFunc: defaultRateLimitFunc,
UserAgent: defaultUserAgent,
}
c.common.client = c
c.APIKeys = (*APIKeysService)(&c.common)
c.DataFeeds = (*DataFeedsService)(&c.common)
c.DataSources = (*DataSourcesService)(&c.common)
c.Jobs = (*JobsService)(&c.common)
c.Notifications = (*NotificationsService)(&c.common)
c.Records = (*RecordsService)(&c.common)
c.Settings = (*SettingsService)(&c.common)
c.Teams = (*TeamsService)(&c.common)
c.Users = (*UsersService)(&c.common)
c.Warnings = (*WarningsService)(&c.common)
c.Zones = (*ZonesService)(&c.common)
for _, option := range options {
option(c)
}
return c
}
type service struct {
client *Client
}
// SetHTTPClient sets a Client instances' httpClient.
func SetHTTPClient(httpClient Doer) func(*Client) {
return func(c *Client) { c.httpClient = httpClient }
}
// SetAPIKey sets a Client instances' APIKey.
func SetAPIKey(key string) func(*Client) {
return func(c *Client) { c.APIKey = key }
}
// SetEndpoint sets a Client instances' Endpoint.
func SetEndpoint(endpoint string) func(*Client) {
return func(c *Client) { c.Endpoint, _ = url.Parse(endpoint) }
}
// SetUserAgent sets a Client instances' user agent.
func SetUserAgent(ua string) func(*Client) {
return func(c *Client) { c.UserAgent = ua }
}
// SetRateLimitFunc sets a Client instances' RateLimitFunc.
func SetRateLimitFunc(ratefunc func(rl RateLimit)) func(*Client) {
return func(c *Client) { c.RateLimitFunc = ratefunc }
}
// Do satisfies the Doer interface.
func (c Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = CheckResponse(resp)
if err != nil {
return resp, err
}
rl := parseRate(resp)
c.RateLimitFunc(rl)
if v != nil {
// Try to unmarshal body into given type using streaming decoder.
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
return nil, err
}
}
return resp, err
}
// NewRequest constructs and returns a http.Request.
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(path)
if err != nil {
return nil, err
}
uri := c.Endpoint.ResolveReference(rel)
// Encode body as json
buf := new(bytes.Buffer)
if body != nil {
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, uri.String(), buf)
if err != nil {
return nil, err
}
req.Header.Add(headerAuth, c.APIKey)
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Response wraps stdlib http response.
type Response struct {
*http.Response
}
// Error contains all http responses outside the 2xx range.
type Error struct {
Resp *http.Response
Message string
}
// Satisfy std lib error interface.
func (re *Error) Error() string {
return fmt.Sprintf("%v %v: %d %v", re.Resp.Request.Method, re.Resp.Request.URL, re.Resp.StatusCode, re.Message)
}
// CheckResponse handles parsing of rest api errors. Returns nil if no error.
func CheckResponse(resp *http.Response) error {
if c := resp.StatusCode; c >= 200 && c <= 299 {
return nil
}
restErr := &Error{Resp: resp}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if len(b) == 0 {
return restErr
}
err = json.Unmarshal(b, restErr)
if err != nil {
return err
}
return restErr
}
// RateLimitFunc is rate limiting strategy for the Client instance.
type RateLimitFunc func(RateLimit)
// RateLimit stores X-Ratelimit-* headers
type RateLimit struct {
Limit int
Remaining int
Period int
}
var defaultRateLimitFunc = func(rl RateLimit) {}
// PercentageLeft returns the ratio of Remaining to Limit as a percentage
func (rl RateLimit) PercentageLeft() int {
return rl.Remaining * 100 / rl.Limit
}
// WaitTime returns the time.Duration ratio of Period to Limit
func (rl RateLimit) WaitTime() time.Duration {
return (time.Second * time.Duration(rl.Period)) / time.Duration(rl.Limit)
}
// WaitTimeRemaining returns the time.Duration ratio of Period to Remaining
func (rl RateLimit) WaitTimeRemaining() time.Duration {
return (time.Second * time.Duration(rl.Period)) / time.Duration(rl.Remaining)
}
// RateLimitStrategySleep sets RateLimitFunc to sleep by WaitTimeRemaining
func (c *Client) RateLimitStrategySleep() {
c.RateLimitFunc = func(rl RateLimit) {
remaining := rl.WaitTimeRemaining()
time.Sleep(remaining)
}
}
// parseRate parses rate related headers from http response.
func parseRate(resp *http.Response) RateLimit {
var rl RateLimit
if limit := resp.Header.Get(headerRateLimit); limit != "" {
rl.Limit, _ = strconv.Atoi(limit)
}
if remaining := resp.Header.Get(headerRateRemaining); remaining != "" {
rl.Remaining, _ = strconv.Atoi(remaining)
}
if period := resp.Header.Get(headerRatePeriod); period != "" {
rl.Period, _ = strconv.Atoi(period)
}
return rl
}
// SetTimeParam sets a url timestamp query param given the parameters name.
func SetTimeParam(key string, t time.Time) func(*url.Values) {
return func(v *url.Values) { v.Set(key, strconv.Itoa(int(t.Unix()))) }
}
// SetBoolParam sets a url boolean query param given the parameters name.
func SetBoolParam(key string, b bool) func(*url.Values) {
return func(v *url.Values) { v.Set(key, strconv.FormatBool(b)) }
}
// SetStringParam sets a url string query param given the parameters name.
func SetStringParam(key, val string) func(*url.Values) {
return func(v *url.Values) { v.Set(key, val) }
}